Files
amcs/internal/metadata/normalize_test.go

194 lines
5.8 KiB
Go

package metadata
import (
"strings"
"testing"
"time"
"github.com/google/uuid"
"git.warky.dev/wdevs/amcs/internal/config"
thoughttypes "git.warky.dev/wdevs/amcs/internal/types"
)
func testCaptureConfig() config.CaptureConfig {
return config.CaptureConfig{
Source: "mcp",
MetadataDefaults: config.CaptureMetadataDefault{
Type: "observation",
TopicFallback: "uncategorized",
},
}
}
func TestFallbackUsesConfiguredDefaults(t *testing.T) {
got := Fallback(testCaptureConfig())
if got.Type != "observation" {
t.Fatalf("Fallback type = %q, want observation", got.Type)
}
if len(got.Topics) != 1 || got.Topics[0] != "uncategorized" {
t.Fatalf("Fallback topics = %#v, want [uncategorized]", got.Topics)
}
if got.Source != "mcp" {
t.Fatalf("Fallback source = %q, want mcp", got.Source)
}
if got.MetadataStatus != MetadataStatusComplete {
t.Fatalf("Fallback metadata status = %q, want complete", got.MetadataStatus)
}
}
func TestNormalizeTrimsDedupesAndCapsTopics(t *testing.T) {
topics := []string{}
for i := 0; i < 12; i++ {
topics = append(topics, strings.TrimSpace(" topic "))
topics = append(topics, string(rune('a'+i)))
}
got := Normalize(thoughttypes.ThoughtMetadata{
People: []string{" Alice ", "alice", "", "Bob"},
Topics: topics,
Type: "INVALID",
}, testCaptureConfig())
if len(got.People) != 2 {
t.Fatalf("People len = %d, want 2", len(got.People))
}
if got.Type != "observation" {
t.Fatalf("Type = %q, want observation", got.Type)
}
if len(got.Topics) != maxTopics {
t.Fatalf("Topics len = %d, want %d", len(got.Topics), maxTopics)
}
}
func TestMergeAddsPatchAndNormalizes(t *testing.T) {
base := thoughttypes.ThoughtMetadata{
People: []string{"Alice"},
Topics: []string{"go"},
Type: "idea",
Source: "mcp",
}
patch := thoughttypes.ThoughtMetadata{
People: []string{" Bob ", "alice"},
Topics: []string{"testing"},
Type: "task",
}
got := Merge(base, patch, testCaptureConfig())
if got.Type != "task" {
t.Fatalf("Type = %q, want task", got.Type)
}
if len(got.People) != 2 {
t.Fatalf("People len = %d, want 2", len(got.People))
}
if len(got.Topics) != 2 {
t.Fatalf("Topics len = %d, want 2", len(got.Topics))
}
}
func TestNormalizeDedupesAttachmentsByFileID(t *testing.T) {
id := uuid.New()
got := Normalize(thoughttypes.ThoughtMetadata{
Attachments: []thoughttypes.ThoughtAttachment{
{FileID: id, Name: " one.png ", MediaType: " image/png ", Kind: " image ", SizeBytes: 12, SHA256: " abc "},
{FileID: id, Name: "two.png", MediaType: "image/png", Kind: "image", SizeBytes: 99, SHA256: "def"},
},
}, testCaptureConfig())
if len(got.Attachments) != 1 {
t.Fatalf("Attachments len = %d, want 1", len(got.Attachments))
}
if got.Attachments[0].Name != "one.png" {
t.Fatalf("Attachment name = %q, want one.png", got.Attachments[0].Name)
}
if got.Attachments[0].Kind != "image" {
t.Fatalf("Attachment kind = %q, want image", got.Attachments[0].Kind)
}
}
func TestSanitizeExtractedDropsAttachmentsAndMetadataControlFields(t *testing.T) {
id := uuid.New()
got := SanitizeExtracted(thoughttypes.ThoughtMetadata{
Type: "idea",
Attachments: []thoughttypes.ThoughtAttachment{{FileID: id, Name: "secret.pdf"}},
MetadataStatus: MetadataStatusFailed,
MetadataUpdatedAt: "2026-03-30T10:00:00Z",
MetadataLastAttemptedAt: "2026-03-30T10:01:00Z",
MetadataError: "boom",
})
if len(got.Attachments) != 0 {
t.Fatalf("Attachments len = %d, want 0", len(got.Attachments))
}
if got.MetadataStatus != "" {
t.Fatalf("MetadataStatus = %q, want empty", got.MetadataStatus)
}
if got.MetadataUpdatedAt != "" {
t.Fatalf("MetadataUpdatedAt = %q, want empty", got.MetadataUpdatedAt)
}
if got.MetadataLastAttemptedAt != "" {
t.Fatalf("MetadataLastAttemptedAt = %q, want empty", got.MetadataLastAttemptedAt)
}
if got.MetadataError != "" {
t.Fatalf("MetadataError = %q, want empty", got.MetadataError)
}
if got.Type != "idea" {
t.Fatalf("Type = %q, want idea", got.Type)
}
}
func TestMarkMetadataPendingTracksAttemptWithoutClearingPreviousSuccess(t *testing.T) {
attempt := time.Date(2026, 3, 30, 10, 0, 0, 0, time.UTC)
base := thoughttypes.ThoughtMetadata{
Topics: []string{"go"},
MetadataUpdatedAt: "2026-03-29T10:00:00Z",
}
got := MarkMetadataPending(base, testCaptureConfig(), attempt, errTestMetadataFailure)
if got.MetadataStatus != MetadataStatusPending {
t.Fatalf("MetadataStatus = %q, want pending", got.MetadataStatus)
}
if got.MetadataUpdatedAt != "2026-03-29T10:00:00Z" {
t.Fatalf("MetadataUpdatedAt = %q, want previous success timestamp", got.MetadataUpdatedAt)
}
if got.MetadataLastAttemptedAt != "2026-03-30T10:00:00Z" {
t.Fatalf("MetadataLastAttemptedAt = %q, want attempt timestamp", got.MetadataLastAttemptedAt)
}
if got.MetadataError == "" {
t.Fatal("MetadataError is empty, want failure message")
}
}
func TestMarkMetadataCompleteClearsErrorAndSetsTimestamps(t *testing.T) {
attempt := time.Date(2026, 3, 30, 10, 0, 0, 0, time.UTC)
got := MarkMetadataComplete(thoughttypes.ThoughtMetadata{
Topics: []string{"go"},
MetadataStatus: MetadataStatusFailed,
MetadataError: "timeout",
}, testCaptureConfig(), attempt)
if got.MetadataStatus != MetadataStatusComplete {
t.Fatalf("MetadataStatus = %q, want complete", got.MetadataStatus)
}
if got.MetadataUpdatedAt != "2026-03-30T10:00:00Z" {
t.Fatalf("MetadataUpdatedAt = %q, want completion timestamp", got.MetadataUpdatedAt)
}
if got.MetadataLastAttemptedAt != "2026-03-30T10:00:00Z" {
t.Fatalf("MetadataLastAttemptedAt = %q, want completion timestamp", got.MetadataLastAttemptedAt)
}
if got.MetadataError != "" {
t.Fatalf("MetadataError = %q, want empty", got.MetadataError)
}
}
var errTestMetadataFailure = testError("timeout")
type testError string
func (e testError) Error() string {
return string(e)
}