###############################################################################
# DeepDrftAPI — Phase 9 smoke tests
# IDE: VS 2022 / Rider / VS Code REST Client
#
# HOW TO USE:
#   1. Set @apiKey below. The real value is in DeepDrftAPI/environment/apikey.json
#      under the key "ApiKeySettings.ApiKey". Do NOT commit a real key here.
#   2. Adjust @releaseId, @trackId, and @entryKey to IDs present in your DB.
#   3. Point the multipart upload requests at a real local file before sending.
#
# AUTH NOTE:
#   Unauthenticated endpoints have no ApiKey header.
#   ApiKey-gated endpoints use header "ApiKey: {{apiKey}}" (header name is literal
#   "ApiKey", not "X-Api-Key" — confirmed in ApiKeyAuthenticationMiddleware.cs).
###############################################################################

@host = http://localhost:5003

# REPLACE with value from DeepDrftAPI/environment/apikey.json → ApiKeySettings.ApiKey
@apiKey = REPLACE_WITH_YOUR_API_KEY

# Placeholders — edit these to match IDs in your local DB
@releaseId = 1
@trackId = 1
@entryKey = replace-with-real-entry-key


###############################################################################
# 1. RELEASE READS
###############################################################################

### 1a. List all releases (unauth) — expect 200 PagedResult
# exercises: GET api/release (no medium filter)
GET {{host}}/api/release
Accept: application/json

###

### 1b. List Session releases (unauth) — expect 200, only Session medium returned
# exercises: GET api/release?medium=session
GET {{host}}/api/release?medium=session
Accept: application/json

###

### 1c. List Mix releases (unauth) — expect 200, only Mix medium returned
# exercises: GET api/release?medium=mix
GET {{host}}/api/release?medium=mix
Accept: application/json

###

### 1d. List Cut releases (unauth) — expect 200, only Cut medium returned
# exercises: GET api/release?medium=cut
GET {{host}}/api/release?medium=cut
Accept: application/json

###

### 1e. Bad medium value (unauth) — expect 400 "Unrecognised medium: bogus"
# exercises: GET api/release?medium=bogus → BadRequest branch in ReleaseController
GET {{host}}/api/release?medium=bogus
Accept: application/json

###

### 1f. Single release by id (unauth) — expect 200 ReleaseDto with both metadata navs
# exercises: GET api/release/{id:long}
GET {{host}}/api/release/{{releaseId}}
Accept: application/json

###


###############################################################################
# 2. TRACK READS
###############################################################################

### 2a. Paged track list — all tracks (unauth) — expect 200 PagedResult<TrackDto>
# exercises: GET api/track/page (no filters)
GET {{host}}/api/track/page
Accept: application/json

###

### 2b. Paged track list filtered by releaseId (unauth) — expect 200, tracks for that release
# exercises: GET api/track/page?releaseId=
GET {{host}}/api/track/page?releaseId={{releaseId}}
Accept: application/json

###

### 2c. Paged track list filtered by album title (unauth) — expect 200
# exercises: GET api/track/page?album=
GET {{host}}/api/track/page?album=My+Album+Name
Accept: application/json

###

### 2d. Paged track list filtered by genre (unauth) — expect 200
# exercises: GET api/track/page?genre=
GET {{host}}/api/track/page?genre=Electronic
Accept: application/json

###

### 2e. Single track metadata by SQL id (ApiKey) — expect 200 TrackDto
# exercises: GET api/track/meta/{id:long}
GET {{host}}/api/track/meta/{{trackId}}
ApiKey: {{apiKey}}
Accept: application/json

###

### 2f. Track audio stream — full file (unauth) — expect 200 with audio bytes
# exercises: GET api/track/{trackId} → streams WAV from FileDatabase vault
GET {{host}}/api/track/{{entryKey}}
Accept: audio/wav

###

### 2g. Track audio stream — Range request (unauth) — expect 206 Partial Content
# exercises: GET api/track/{trackId} with Range header → 206 + byte slice
GET {{host}}/api/track/{{entryKey}}
Range: bytes=44-
Accept: audio/wav

###

### 2h. Albums list (unauth) — expect 200 List<ReleaseDto> with per-release track counts
# exercises: GET api/track/albums
GET {{host}}/api/track/albums
Accept: application/json

###

### 2i. Genres list (unauth) — expect 200 List<string> distinct genres
# exercises: GET api/track/genres
GET {{host}}/api/track/genres
Accept: application/json

###

### 2j. Random track (unauth) — expect 200 TrackDto (or 404 when library is empty)
# exercises: GET api/track/random
GET {{host}}/api/track/random
Accept: application/json

###

### 2k. Waveform profile for track (unauth) — expect 200 WaveformProfileDto or 404 if not computed
# exercises: GET api/track/{trackId}/waveform
GET {{host}}/api/track/{{entryKey}}/waveform
Accept: application/json

###

### 2l. Waveform status for all tracks (ApiKey) — expect 200 List<WaveformStatusDto>
# exercises: GET api/track/waveform-status (admin backfill view)
GET {{host}}/api/track/waveform-status
ApiKey: {{apiKey}}
Accept: application/json

###


###############################################################################
# 3. MEDIUM WRITE PATH
###############################################################################

### 3a. Upload a Cut track (ApiKey, multipart) — expect 200 TrackDto
# exercises: POST api/track/upload with medium=Cut
# REPLACE ./path/to/test.wav with a real local .wav (or .mp3 / .flac) file path.
POST {{host}}/api/track/upload
ApiKey: {{apiKey}}
Content-Type: multipart/form-data; boundary=boundary

--boundary
Content-Disposition: form-data; name="audioFile"; filename="test.wav"
Content-Type: audio/wav

< ./path/to/test.wav
--boundary
Content-Disposition: form-data; name="trackName"

Smoke Test Cut
--boundary
Content-Disposition: form-data; name="artist"

Test Artist
--boundary
Content-Disposition: form-data; name="album"

Smoke Test Album
--boundary
Content-Disposition: form-data; name="genre"

Electronic
--boundary
Content-Disposition: form-data; name="releaseDate"

2025-01-01
--boundary
Content-Disposition: form-data; name="createdByUserId"

1
--boundary
Content-Disposition: form-data; name="medium"

Cut
--boundary
Content-Disposition: form-data; name="releaseType"

Single
--boundary
Content-Disposition: form-data; name="trackNumber"

1
--boundary--

###

### 3b. Upload a Session track (ApiKey, multipart) — expect 200 TrackDto
# exercises: POST api/track/upload with medium=Session
# NOTE: Session releases are single-track. Use a unique album name to create a new release.
# REPLACE ./path/to/test.wav with a real local .wav/.mp3/.flac file.
POST {{host}}/api/track/upload
ApiKey: {{apiKey}}
Content-Type: multipart/form-data; boundary=boundary

--boundary
Content-Disposition: form-data; name="audioFile"; filename="test.wav"
Content-Type: audio/wav

< ./path/to/test.wav
--boundary
Content-Disposition: form-data; name="trackName"

Smoke Test Session
--boundary
Content-Disposition: form-data; name="artist"

Test Artist
--boundary
Content-Disposition: form-data; name="album"

Smoke Session Album 001
--boundary
Content-Disposition: form-data; name="createdByUserId"

1
--boundary
Content-Disposition: form-data; name="medium"

Session
--boundary
Content-Disposition: form-data; name="trackNumber"

1
--boundary--

###

### 3c. Upload a Mix track (ApiKey, multipart) — expect 200 TrackDto
# exercises: POST api/track/upload with medium=Mix
# NOTE: Mix releases are also single-track. Use a unique album name.
# REPLACE ./path/to/test.wav with a real local .wav/.mp3/.flac file.
POST {{host}}/api/track/upload
ApiKey: {{apiKey}}
Content-Type: multipart/form-data; boundary=boundary

--boundary
Content-Disposition: form-data; name="audioFile"; filename="test.wav"
Content-Type: audio/wav

< ./path/to/test.wav
--boundary
Content-Disposition: form-data; name="trackName"

Smoke Test Mix
--boundary
Content-Disposition: form-data; name="artist"

Test Artist
--boundary
Content-Disposition: form-data; name="album"

Smoke Mix Album 001
--boundary
Content-Disposition: form-data; name="createdByUserId"

1
--boundary
Content-Disposition: form-data; name="medium"

Mix
--boundary
Content-Disposition: form-data; name="trackNumber"

1
--boundary--

###

### 3d. Update track metadata — flip Medium to Session (ApiKey, JSON) — expect 200
# exercises: PUT api/track/meta/{id:long}
# Medium: null means "no change"; provide it to change the release medium.
PUT {{host}}/api/track/meta/{{trackId}}
ApiKey: {{apiKey}}
Content-Type: application/json

{
  "TrackName": "Updated Track Name",
  "Artist": "Updated Artist",
  "Album": "Updated Album",
  "Genre": "Electronic",
  "ReleaseDate": "2025-06-01",
  "ImagePath": null,
  "ReleaseType": null,
  "Medium": "Session",
  "TrackNumber": 1
}

###


###############################################################################
# 4. WAVE 7 — SESSION CARDINALITY 409 SEQUENCE
#
# Session and Mix releases enforce a single-track maximum. The sequence below
# demonstrates the enforcement:
#   Step 1: upload the FIRST track to a Session release → expect 200
#   Step 2: upload a SECOND track to the SAME album+artist → expect 409 Conflict
#
# The 409 body will contain: "A Session release holds a single track; '<album>'
# already has one — edit the existing track or choose a different release."
#
# Cut-medium releases have no cardinality limit, so an identical sequence with
# medium=Cut would produce 200 on both requests.
###############################################################################

### 4a. [STEP 1] Upload first Session track — expect 200 TrackDto
# The album "Cardinality Test Session" does not yet exist; this creates it.
# REPLACE ./path/to/test.wav with a real local file.
POST {{host}}/api/track/upload
ApiKey: {{apiKey}}
Content-Type: multipart/form-data; boundary=boundary

--boundary
Content-Disposition: form-data; name="audioFile"; filename="test.wav"
Content-Type: audio/wav

< ./path/to/test.wav
--boundary
Content-Disposition: form-data; name="trackName"

Cardinality Track 1
--boundary
Content-Disposition: form-data; name="artist"

Cardinality Artist
--boundary
Content-Disposition: form-data; name="album"

Cardinality Test Session
--boundary
Content-Disposition: form-data; name="createdByUserId"

1
--boundary
Content-Disposition: form-data; name="medium"

Session
--boundary
Content-Disposition: form-data; name="trackNumber"

1
--boundary--

###

### 4b. [STEP 2] Upload second Session track to the SAME album+artist — expect 409 Conflict
# Same album "Cardinality Test Session", same artist "Cardinality Artist".
# UnifiedTrackService.UploadAsync pre-checks cardinality before the vault write.
# REPLACE ./path/to/test.wav with a real local file.
POST {{host}}/api/track/upload
ApiKey: {{apiKey}}
Content-Type: multipart/form-data; boundary=boundary

--boundary
Content-Disposition: form-data; name="audioFile"; filename="test.wav"
Content-Type: audio/wav

< ./path/to/test.wav
--boundary
Content-Disposition: form-data; name="trackName"

Cardinality Track 2
--boundary
Content-Disposition: form-data; name="artist"

Cardinality Artist
--boundary
Content-Disposition: form-data; name="album"

Cardinality Test Session
--boundary
Content-Disposition: form-data; name="createdByUserId"

1
--boundary
Content-Disposition: form-data; name="medium"

Session
--boundary
Content-Disposition: form-data; name="trackNumber"

2
--boundary--

###


###############################################################################
# 5. MIX WAVEFORM
###############################################################################

### 5a. Trigger waveform generation for a Mix release (ApiKey, no body) — expect 200 or 404
# exercises: POST api/release/{id:long}/mix/waveform
# 404 if the release is not a Mix, has no track, or the track has no audio stored.
POST {{host}}/api/release/{{releaseId}}/mix/waveform
ApiKey: {{apiKey}}
Content-Length: 0

###

### 5b. Fetch stored mix waveform (unauth) — expect 200 WaveformProfileDto or 404
# exercises: GET api/release/{id:long}/mix/waveform
# 404 when release is not a Mix, has no WaveformEntryKey, or datum not yet computed (run 5a first).
GET {{host}}/api/release/{{releaseId}}/mix/waveform
Accept: application/json

###


###############################################################################
# 6. SESSION HERO IMAGE
###############################################################################

### 6a. Upload hero image for a Session release (ApiKey, multipart) — expect 200 or 404
# exercises: POST api/release/{id:long}/session/hero-image
# 404 if release not found. 400 if no image file or unsupported content type.
# The release must be a Session (enforced in UnifiedReleaseService.SetHeroImageAsync).
# REPLACE ./path/to/hero.jpg with a real local JPEG or PNG file.
POST {{host}}/api/release/{{releaseId}}/session/hero-image
ApiKey: {{apiKey}}
Content-Type: multipart/form-data; boundary=boundary

--boundary
Content-Disposition: form-data; name="image"; filename="hero.jpg"
Content-Type: image/jpeg

< ./path/to/hero.jpg
--boundary--

###
