package kc
import (
"archive/zip"
"bytes"
"encoding/json"
"testing"
)
// buildZip creates a ZIP archive in memory from a map of filename → content.
func buildZip(t *testing.T, files map[string][]byte) []byte {
t.Helper()
var buf bytes.Buffer
w := zip.NewWriter(&buf)
for name, content := range files {
f, err := w.Create(name)
if err != nil {
t.Fatalf("creating zip entry %s: %v", name, err)
}
if _, err := f.Write(content); err != nil {
t.Fatalf("writing zip entry %s: %v", name, err)
}
}
if err := w.Close(); err != nil {
t.Fatalf("closing zip: %v", err)
}
return buf.Bytes()
}
func mustJSON(t *testing.T, v any) []byte {
t.Helper()
data, err := json.Marshal(v)
if err != nil {
t.Fatalf("marshaling JSON: %v", err)
}
return data
}
func TestExtract_PlainFCStd(t *testing.T) {
data := buildZip(t, map[string][]byte{
"Document.xml": []byte(""),
"thumbnails/a.png": []byte("png"),
})
result, err := Extract(data)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result != nil {
t.Fatalf("expected nil result for plain .fcstd, got %+v", result)
}
}
func TestExtract_ValidKC(t *testing.T) {
manifest := Manifest{
UUID: "550e8400-e29b-41d4-a716-446655440000",
KCVersion: "1.0",
RevisionHash: "abc123",
SiloInstance: "https://silo.example.com",
}
metadata := Metadata{
SchemaName: "mechanical-part-v2",
Tags: []string{"structural", "aluminum"},
LifecycleState: "draft",
Fields: map[string]any{
"material": "6061-T6",
"weight_kg": 0.34,
},
}
data := buildZip(t, map[string][]byte{
"Document.xml": []byte(""),
"silo/manifest.json": mustJSON(t, manifest),
"silo/metadata.json": mustJSON(t, metadata),
})
result, err := Extract(data)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result == nil {
t.Fatal("expected non-nil result")
}
if result.Manifest == nil {
t.Fatal("expected manifest")
}
if result.Manifest.UUID != manifest.UUID {
t.Errorf("manifest UUID = %q, want %q", result.Manifest.UUID, manifest.UUID)
}
if result.Manifest.KCVersion != manifest.KCVersion {
t.Errorf("manifest KCVersion = %q, want %q", result.Manifest.KCVersion, manifest.KCVersion)
}
if result.Manifest.RevisionHash != manifest.RevisionHash {
t.Errorf("manifest RevisionHash = %q, want %q", result.Manifest.RevisionHash, manifest.RevisionHash)
}
if result.Manifest.SiloInstance != manifest.SiloInstance {
t.Errorf("manifest SiloInstance = %q, want %q", result.Manifest.SiloInstance, manifest.SiloInstance)
}
if result.Metadata == nil {
t.Fatal("expected metadata")
}
if result.Metadata.SchemaName != metadata.SchemaName {
t.Errorf("metadata SchemaName = %q, want %q", result.Metadata.SchemaName, metadata.SchemaName)
}
if result.Metadata.LifecycleState != metadata.LifecycleState {
t.Errorf("metadata LifecycleState = %q, want %q", result.Metadata.LifecycleState, metadata.LifecycleState)
}
if len(result.Metadata.Tags) != 2 {
t.Errorf("metadata Tags len = %d, want 2", len(result.Metadata.Tags))
}
if result.Metadata.Fields["material"] != "6061-T6" {
t.Errorf("metadata Fields[material] = %v, want 6061-T6", result.Metadata.Fields["material"])
}
}
func TestExtract_ManifestOnly(t *testing.T) {
manifest := Manifest{
UUID: "550e8400-e29b-41d4-a716-446655440000",
KCVersion: "1.0",
}
data := buildZip(t, map[string][]byte{
"Document.xml": []byte(""),
"silo/manifest.json": mustJSON(t, manifest),
})
result, err := Extract(data)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result == nil {
t.Fatal("expected non-nil result")
}
if result.Manifest == nil {
t.Fatal("expected manifest")
}
if result.Metadata != nil {
t.Errorf("expected nil metadata, got %+v", result.Metadata)
}
}
func TestExtract_InvalidJSON(t *testing.T) {
data := buildZip(t, map[string][]byte{
"silo/manifest.json": []byte("{not valid json"),
})
result, err := Extract(data)
if err == nil {
t.Fatal("expected error for invalid JSON")
}
if result != nil {
t.Errorf("expected nil result on error, got %+v", result)
}
}
func TestExtract_NotAZip(t *testing.T) {
result, err := Extract([]byte("this is not a zip file"))
if err == nil {
t.Fatal("expected error for non-ZIP data")
}
if result != nil {
t.Errorf("expected nil result on error, got %+v", result)
}
}
func TestExtract_EmptySiloDir(t *testing.T) {
// silo/ directory entry exists but no manifest or metadata files
data := buildZip(t, map[string][]byte{
"Document.xml": []byte(""),
"silo/": {},
})
result, err := Extract(data)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result == nil {
t.Fatal("expected non-nil result for silo/ dir")
}
if result.Manifest != nil {
t.Errorf("expected nil manifest, got %+v", result.Manifest)
}
if result.Metadata != nil {
t.Errorf("expected nil metadata, got %+v", result.Metadata)
}
}