# Exam Sprint Download URL Prefix Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Rename the Azure report download URL configuration from `url-prefix` to `download-url-prefix` and make test downloads return direct Azure Blob URLs.

**Architecture:** Keep the existing storage boundary unchanged: `DefaultExamSprintReportApplicationService` asks `ExamSprintReportStorage.generateDownloadUrl(...)`, and `AzureBlobExamSprintReportStorage` builds a public URL from a configured download prefix, container name, and storage object key. This change only clarifies the configuration name and default test URL; upload/download/delete continue to use the Azure SDK client and connection string.

**Tech Stack:** Java 17, Spring Boot `@Value` configuration binding, Maven, JUnit 5, AssertJ, Mockito, Spring MockMvc tests.

---

## File Structure

- Modify `abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportProperties.java`
  - Replace the `Storage.urlPrefix` property with `Storage.downloadUrlPrefix`.
- Modify `abilities/exam-sprint/infrastructure/src/main/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/storage/AzureBlobExamSprintReportStorage.java`
  - Inject `ability.exam-sprint.report.storage.download-url-prefix` instead of `url-prefix`.
  - Rename internal field and validation method naming from `urlPrefix` to `downloadUrlPrefix`.
- Modify `ability-center-runtime/src/main/resources/application-test.yml`
  - Replace `url-prefix` with `download-url-prefix` and default to `https://dcjxbtest.blob.core.chinacloudapi.cn`.
- Modify `ability-center-runtime/src/main/resources/application-prod.yml`
  - Replace `url-prefix` with `download-url-prefix` and keep the existing production Blob account host default.
- Modify `abilities/exam-sprint/infrastructure/src/test/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/storage/AzureBlobExamSprintReportStorageTest.java`
  - Update test names, expected URLs, and validation messages.
- Modify `ability-center-runtime/src/test/java/cn/yunzhixue/ability/center/examsprint/adapter/http/ExamSprintReportControllerWebMvcTest.java`
  - Update expected `downloadUrl` values from CDN host to Blob host where tests assert exact values.

### Task 1: Rename the Azure storage configuration property

**Files:**
- Modify: `abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportProperties.java`
- Modify: `abilities/exam-sprint/infrastructure/src/main/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/storage/AzureBlobExamSprintReportStorage.java`
- Modify: `ability-center-runtime/src/main/resources/application-test.yml`
- Modify: `ability-center-runtime/src/main/resources/application-prod.yml`
- Test: `abilities/exam-sprint/infrastructure/src/test/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/storage/AzureBlobExamSprintReportStorageTest.java`

- [ ] **Step 1: Write the failing infrastructure expectations**

In `AzureBlobExamSprintReportStorageTest`, update the constructor reflection test so it still expects seven parameters but treats the sixth `String` as `download-url-prefix` by name in the surrounding test naming. Rename `generateDownloadUrlReturnsConfiguredOssFileLink` to `generateDownloadUrlReturnsConfiguredBlobFileLink` and change the expected URL to:

```java
assertThat(downloadUrl).isEqualTo(URI.create(
        "https://dcjxbtest.blob.core.chinacloudapi.cn/exam-assault-report/exam-sprint-outlook-report-report-123.pdf"));
```

Rename `constructorRejectsAllSlashUrlPrefix` to `constructorRejectsAllSlashDownloadUrlPrefix` and `constructorRejectsBlankAfterTrimUrlPrefix` to `constructorRejectsBlankAfterTrimDownloadUrlPrefix`. Change both expected messages to:

```java
.hasMessage("Azure storage download-url-prefix is incomplete");
```

- [ ] **Step 2: Run the focused infrastructure test and verify it fails**

Run:

```bash
mvn -pl abilities/exam-sprint/infrastructure -Dtest=AzureBlobExamSprintReportStorageTest test
```

Expected: FAIL because production code still emits the old CDN URL and old `url-prefix` error message.

- [ ] **Step 3: Implement the Java property rename**

In `ExamSprintReportProperties.Storage`, replace:

```java
private String urlPrefix;

public String getUrlPrefix() {
    return urlPrefix;
}

public void setUrlPrefix(String urlPrefix) {
    this.urlPrefix = urlPrefix;
}
```

with:

```java
private String downloadUrlPrefix;

public String getDownloadUrlPrefix() {
    return downloadUrlPrefix;
}

public void setDownloadUrlPrefix(String downloadUrlPrefix) {
    this.downloadUrlPrefix = downloadUrlPrefix;
}
```

In `AzureBlobExamSprintReportStorage`, replace the `urlPrefix` field and constructor parameter with `downloadUrlPrefix`, inject:

```java
@Value("${ability.exam-sprint.report.storage.download-url-prefix:}") String downloadUrlPrefix,
```

and change URL creation to:

```java
return URI.create(downloadUrlPrefix + "/" + containerName + "/" + normalizedStorageObjectKey);
```

Rename `normalizeUrlPrefix` to `normalizeDownloadUrlPrefix` and make both thrown messages say:

```java
throw new IllegalStateException("Azure storage download-url-prefix is incomplete");
```

- [ ] **Step 4: Update runtime YAML configuration**

In `application-test.yml`, replace:

```yaml
url-prefix: "${AZURE_BLOB_URL_PREFIX:https://dcjxb-cdntest.yunzhixue.cn}"
```

with:

```yaml
download-url-prefix: "${AZURE_BLOB_DOWNLOAD_URL_PREFIX:https://dcjxbtest.blob.core.chinacloudapi.cn}"
```

In `application-prod.yml`, replace:

```yaml
url-prefix: "${AZURE_BLOB_URL_PREFIX:https://dcjxb.blob.core.chinacloudapi.cn}"
```

with:

```yaml
download-url-prefix: "${AZURE_BLOB_DOWNLOAD_URL_PREFIX:https://dcjxb.blob.core.chinacloudapi.cn}"
```

- [ ] **Step 5: Run the focused infrastructure test and verify it passes**

Run:

```bash
mvn -pl abilities/exam-sprint/infrastructure -Dtest=AzureBlobExamSprintReportStorageTest test
```

Expected: PASS with `Tests run: 8, Failures: 0, Errors: 0, Skipped: 0`.

### Task 2: Update HTTP-facing tests for the new download host

**Files:**
- Modify: `ability-center-runtime/src/test/java/cn/yunzhixue/ability/center/examsprint/adapter/http/ExamSprintReportControllerWebMvcTest.java`

- [ ] **Step 1: Write the failing WebMvc expectations**

Replace exact expected URLs using `https://dcjxb-cdntest.yunzhixue.cn/exam-assault-report/` with `https://dcjxbtest.blob.core.chinacloudapi.cn/exam-assault-report/`. For example:

```java
"https://dcjxbtest.blob.core.chinacloudapi.cn/exam-assault-report/exam-sprint-outlook-report-report-sync-001.pdf"
```

and:

```java
"https://dcjxbtest.blob.core.chinacloudapi.cn/exam-assault-report/exam-sprint-achievement-report-report-sync-002.pdf"
```

- [ ] **Step 2: Run the focused runtime WebMvc test and verify it fails if code/config is incomplete**

Run:

```bash
mvn -pl ability-center-runtime -Dtest=ExamSprintReportControllerWebMvcTest test
```

Expected before Task 1 implementation: FAIL because the old configuration emits CDN URLs. Expected after Task 1 implementation: PASS.

- [ ] **Step 3: Re-run the focused runtime WebMvc test after Task 1 changes**

Run:

```bash
mvn -pl ability-center-runtime -Dtest=ExamSprintReportControllerWebMvcTest test
```

Expected: PASS with all `ExamSprintReportControllerWebMvcTest` cases passing.

### Task 3: Final verification

**Files:**
- Verify only; no additional files should change in this task.

- [ ] **Step 1: Run all directly affected tests**

Run:

```bash
mvn -pl abilities/exam-sprint/infrastructure -Dtest=AzureBlobExamSprintReportStorageTest test && mvn -pl ability-center-runtime -Dtest=ExamSprintReportControllerWebMvcTest test
```

Expected: both Maven commands finish with `BUILD SUCCESS`.

- [ ] **Step 2: Inspect the diff for accidental compatibility code or secret changes**

Run:

```bash
git diff -- abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportProperties.java abilities/exam-sprint/infrastructure/src/main/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/storage/AzureBlobExamSprintReportStorage.java ability-center-runtime/src/main/resources/application-test.yml ability-center-runtime/src/main/resources/application-prod.yml abilities/exam-sprint/infrastructure/src/test/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/storage/AzureBlobExamSprintReportStorageTest.java ability-center-runtime/src/test/java/cn/yunzhixue/ability/center/examsprint/adapter/http/ExamSprintReportControllerWebMvcTest.java
```

Expected: diff contains only the `download-url-prefix` rename, Blob host default for test, unchanged production Blob host default, and test expectation updates.

- [ ] **Step 3: Report deployment configuration note**

Tell the operator that test deployments should use:

```bash
AZURE_BLOB_DOWNLOAD_URL_PREFIX=https://dcjxbtest.blob.core.chinacloudapi.cn
```

and that `AZURE_BLOB_URL_PREFIX` is intentionally no longer read.

## Self-Review

- Spec coverage: The plan covers the approved rename to `download-url-prefix`, direct test Blob URL default, no backward compatibility for `url-prefix`, production config rename, and affected tests.
- Placeholder scan: No placeholder tasks remain; each step lists exact files, code snippets, commands, and expected outcomes.
- Type consistency: The property name is consistently `downloadUrlPrefix` in Java and `download-url-prefix` in YAML/Spring configuration.
