package artifact import ( "context" "errors" "os" "path/filepath" "strings" "testing" "gitea.maximumdirect.net/eric/promptkit/internal/domain" ) func TestCompositeReader_Read(t *testing.T) { reader := NewCompositeReader() ctx := context.Background() t.Run("inline artifact", func(t *testing.T) { ref := domain.ArtifactRef{ Type: domain.ArtifactRefInline, Body: "hello world", } art, err := reader.Read(ctx, ref) if err != nil { t.Fatalf("unexpected error: %v", err) } if string(art.Body) != "hello world" { t.Errorf("expected 'hello world', got %s", string(art.Body)) } if art.ContentType != "text/plain" { t.Errorf("expected text/plain content type, got %q", art.ContentType) } if art.Hash != "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" { t.Errorf("unexpected hash: %s", art.Hash) } if art.Size != int64(len(ref.Body)) { t.Errorf("expected size %d, got %d", len(ref.Body), art.Size) } }) t.Run("inline artifact missing body", func(t *testing.T) { ref := domain.ArtifactRef{ Type: domain.ArtifactRefInline, Body: "", } _, err := reader.Read(ctx, ref) if !errors.Is(err, ErrMissingInlineBody) { t.Errorf("expected ErrMissingInlineBody, got %v", err) } }) t.Run("unsupported ref type", func(t *testing.T) { ref := domain.ArtifactRef{ Type: domain.ArtifactRefType("unsupported"), URI: "unsupported://bucket/key", } _, err := reader.Read(ctx, ref) if !errors.Is(err, ErrUnsupportedRefType) { t.Error("expected error for unsupported type") } }) } func TestCompositeReaderCopiesInlineData(t *testing.T) { reader := NewCompositeReader() ref := domain.ArtifactRef{ Type: domain.ArtifactRefInline, Body: "hello", URI: "inline:greeting", } first, err := reader.Read(context.Background(), ref) if err != nil { t.Fatalf("read first artifact: %v", err) } first.Body[0] = 'j' second, err := reader.Read(context.Background(), ref) if err != nil { t.Fatalf("read second artifact: %v", err) } if got := string(second.Body); got != ref.Body { t.Fatalf("expected an independent body %q, got %q", ref.Body, got) } if second.URI != ref.URI { t.Fatalf("expected URI %q, got %q", ref.URI, second.URI) } } func TestCompositeReaderHonorsCancellation(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) cancel() _, err := NewCompositeReader().Read(ctx, domain.ArtifactRef{ Type: domain.ArtifactRefInline, Body: "ignored", }) if !errors.Is(err, context.Canceled) { t.Fatalf("expected context cancellation, got %v", err) } } func TestFileReader_Read(t *testing.T) { content := []byte("test file content") filePath := filepath.Join(t.TempDir(), "artifact.txt") if err := os.WriteFile(filePath, content, 0o600); err != nil { t.Fatal(err) } reader := NewCompositeReader() ctx := context.Background() t.Run("file artifact loading", func(t *testing.T) { ref := domain.ArtifactRef{ Type: domain.ArtifactRefFile, URI: filePath, } art, err := reader.Read(ctx, ref) if err != nil { t.Fatalf("unexpected error: %v", err) } if string(art.Body) != string(content) { t.Errorf("expected %s, got %s", string(content), string(art.Body)) } if art.Name != filepath.Base(filePath) { t.Errorf("expected name %q, got %q", filepath.Base(filePath), art.Name) } if !strings.HasPrefix(art.ContentType, "text/plain") { t.Errorf("expected text content type, got %q", art.ContentType) } if art.URI != filePath { t.Errorf("expected URI %q, got %q", filePath, art.URI) } if art.Size != int64(len(content)) { t.Errorf("expected size %d, got %d", len(content), art.Size) } if art.Hash != "60f5237ed4049f0382661ef009d2bc42e48c3ceb3edb6600f7024e7ab3b838f3" { t.Errorf("unexpected hash: %s", art.Hash) } }) t.Run("missing file path", func(t *testing.T) { ref := domain.ArtifactRef{ Type: domain.ArtifactRefFile, URI: "", } _, err := reader.Read(ctx, ref) if !errors.Is(err, ErrMissingFilePath) { t.Errorf("expected ErrMissingFilePath, got %v", err) } }) t.Run("missing file", func(t *testing.T) { ref := domain.ArtifactRef{ Type: domain.ArtifactRefFile, URI: filepath.Join(t.TempDir(), "missing.txt"), } if _, err := reader.Read(ctx, ref); err == nil { t.Fatal("expected missing file error") } }) t.Run("unknown extension uses text fallback", func(t *testing.T) { path := filepath.Join(t.TempDir(), "artifact.unknownextension") if err := os.WriteFile(path, content, 0o600); err != nil { t.Fatal(err) } art, err := reader.Read(ctx, domain.ArtifactRef{ Type: domain.ArtifactRefFile, URI: path, }) if err != nil { t.Fatalf("unexpected error: %v", err) } if art.ContentType != "text/plain" { t.Errorf("expected text/plain fallback, got %q", art.ContentType) } }) }