feat: Sample Tag Viewer — read-only tag browser for AKAI disk images
Backend: - extract.go: listTags() navigates to volume, runs akaiutil lstags - extract.go: parseTags() parses tag name/index from lstags output - serve.go: GET /api/disk/partitions — list partitions on an ISO - serve.go: GET /api/disk/volumes — list volumes on a partition - serve.go: GET /api/volume/tags — list tags on a volume Web UI: - Tag Viewer section with ISO path input and Browse button - Partition selector buttons - Volume grid with click-to-select - Tag chips with color-coded dots (20-color palette) Tests: - 6 parseTags tests (basic, spaces, defaults, no tags, empty, no-type) - 1 listTags integration test - 4 API endpoint tests (partitions, missing-ISO, missing-params, tags) Karpathy: read-only view first — no write operations yet (settagi, clrtagi)
This commit is contained in:
@@ -544,4 +544,93 @@ func TestHandleListWavs_NonExistentDir(t *testing.T) {
|
||||
if _, ok := resp["error"]; !ok {
|
||||
t.Error("expected error for nonexistent dir")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleDiskPartitions_ReturnsPartitions(t *testing.T) {
|
||||
origRun := runAkaiutil
|
||||
defer func() { runAkaiutil = origRun }()
|
||||
|
||||
runAkaiutil = func(isoPath, commands string) (string, error) {
|
||||
return `
|
||||
Disk /disk0
|
||||
Total blocks: 0 (0 KB each) 0 KB
|
||||
Partition table:
|
||||
A - S1000 HD (0 blocks)
|
||||
B - S1000 HD (0 blocks)
|
||||
`, nil
|
||||
}
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/disk/partitions?iso=test.iso", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
handleDiskPartitions(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Errorf("expected 200, got %d", rr.Code)
|
||||
}
|
||||
var parts []string
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &parts); err != nil {
|
||||
t.Fatalf("invalid JSON: %v", err)
|
||||
}
|
||||
if len(parts) != 2 {
|
||||
t.Errorf("expected 2 partitions, got %d: %v", len(parts), parts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleDiskPartitions_MissingISO(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/api/disk/partitions", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
handleDiskPartitions(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Errorf("expected 200, got %d", rr.Code)
|
||||
}
|
||||
var resp map[string]any
|
||||
_ = json.Unmarshal(rr.Body.Bytes(), &resp)
|
||||
if _, ok := resp["error"]; !ok {
|
||||
t.Error("expected error for missing iso")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleVolumeTags_MissingParams(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/api/volume/tags", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
handleVolumeTags(rr, req)
|
||||
|
||||
var resp map[string]any
|
||||
_ = json.Unmarshal(rr.Body.Bytes(), &resp)
|
||||
if resp["error"] == nil {
|
||||
t.Error("expected error for missing params")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleVolumeTags_ReturnsTags(t *testing.T) {
|
||||
origRun := runAkaiutil
|
||||
defer func() { runAkaiutil = origRun }()
|
||||
|
||||
runAkaiutil = func(isoPath, commands string) (string, error) {
|
||||
return `
|
||||
/disk0/A/SomeVol > lstags
|
||||
|
||||
tag name
|
||||
-----------------
|
||||
01 Drums
|
||||
02 Bass
|
||||
-----------------
|
||||
`, nil
|
||||
}
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/volume/tags?iso=test.iso&part=A&vol=SomeVol", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
handleVolumeTags(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Errorf("expected 200, got %d", rr.Code)
|
||||
}
|
||||
var tags []TagInfo
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &tags); err != nil {
|
||||
t.Fatalf("invalid JSON: %v", err)
|
||||
}
|
||||
if len(tags) != 2 {
|
||||
t.Errorf("expected 2 tags, got %d", len(tags))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user