Something went wrong. Try again.
A Transparent and Verifiable Way to Sync the AT Protocol's PLC Directory
Something went wrong. Try again.
Go
at did-resolver
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438package bundle_test
import ( "path/filepath" "testing" "time"
"tangled.org/atscan.net/plcbundle/bundle" "tangled.org/atscan.net/plcbundle/plc")
// TestIndex tests index operationsfunc TestIndex(t *testing.T) { t.Run("CreateNewIndex", func(t *testing.T) { idx := bundle.NewIndex("test-origin") if idx == nil { t.Fatal("NewIndex returned nil") } if idx.Version != bundle.INDEX_VERSION { t.Errorf("expected version %s, got %s", bundle.INDEX_VERSION, idx.Version) } if idx.Count() != 0 { t.Errorf("expected empty index, got count %d", idx.Count()) } })
t.Run("AddBundle", func(t *testing.T) { idx := bundle.NewIndex("test-origin") meta := &bundle.BundleMetadata{ BundleNumber: 1, StartTime: time.Now(), EndTime: time.Now().Add(time.Hour), OperationCount: bundle.BUNDLE_SIZE, DIDCount: 1000, Hash: "abc123", CompressedHash: "def456", }
idx.AddBundle(meta)
if idx.Count() != 1 { t.Errorf("expected count 1, got %d", idx.Count()) }
retrieved, err := idx.GetBundle(1) if err != nil { t.Fatalf("GetBundle failed: %v", err) } if retrieved.Hash != meta.Hash { t.Errorf("expected hash %s, got %s", meta.Hash, retrieved.Hash) } })
t.Run("SaveAndLoad", func(t *testing.T) { tmpDir := t.TempDir() indexPath := filepath.Join(tmpDir, "test_index.json")
// Create and save idx := bundle.NewIndex("test-origin") idx.AddBundle(&bundle.BundleMetadata{ BundleNumber: 1, StartTime: time.Now(), EndTime: time.Now().Add(time.Hour), OperationCount: bundle.BUNDLE_SIZE, Hash: "test123", })
if err := idx.Save(indexPath); err != nil { t.Fatalf("Save failed: %v", err) }
// Load loaded, err := bundle.LoadIndex(indexPath) if err != nil { t.Fatalf("LoadIndex failed: %v", err) }
if loaded.Count() != 1 { t.Errorf("expected count 1, got %d", loaded.Count()) } })
t.Run("GetBundleRange", func(t *testing.T) { idx := bundle.NewIndex("test-origin") for i := 1; i <= 5; i++ { idx.AddBundle(&bundle.BundleMetadata{ BundleNumber: i, StartTime: time.Now(), EndTime: time.Now().Add(time.Hour), OperationCount: bundle.BUNDLE_SIZE, }) }
bundles := idx.GetBundleRange(2, 4) if len(bundles) != 3 { t.Errorf("expected 3 bundles, got %d", len(bundles)) } if bundles[0].BundleNumber != 2 || bundles[2].BundleNumber != 4 { t.Errorf("unexpected bundle range") } })
t.Run("FindGaps", func(t *testing.T) { idx := bundle.NewIndex("test-origin") // Add bundles 1, 2, 4, 5 (missing 3) for _, num := range []int{1, 2, 4, 5} { idx.AddBundle(&bundle.BundleMetadata{ BundleNumber: num, StartTime: time.Now(), EndTime: time.Now().Add(time.Hour), OperationCount: bundle.BUNDLE_SIZE, }) }
gaps := idx.FindGaps() if len(gaps) != 1 { t.Errorf("expected 1 gap, got %d", len(gaps)) } if len(gaps) > 0 && gaps[0] != 3 { t.Errorf("expected gap at 3, got %d", gaps[0]) } })}
// TestBundle tests bundle operationsfunc TestBundle(t *testing.T) { t.Run("ValidateForSave", func(t *testing.T) { tests := []struct { name string bundle *bundle.Bundle wantErr bool }{ { name: "valid bundle", bundle: &bundle.Bundle{ BundleNumber: 1, StartTime: time.Now(), EndTime: time.Now().Add(time.Hour), Operations: makeTestOperations(bundle.BUNDLE_SIZE), }, wantErr: false, }, { name: "invalid bundle number", bundle: &bundle.Bundle{ BundleNumber: 0, Operations: makeTestOperations(bundle.BUNDLE_SIZE), }, wantErr: true, }, { name: "wrong operation count", bundle: &bundle.Bundle{ BundleNumber: 1, Operations: makeTestOperations(100), }, wantErr: true, }, { name: "start after end", bundle: &bundle.Bundle{ BundleNumber: 1, StartTime: time.Now().Add(time.Hour), EndTime: time.Now(), Operations: makeTestOperations(bundle.BUNDLE_SIZE), }, wantErr: true, }, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.bundle.ValidateForSave() if (err != nil) != tt.wantErr { t.Errorf("ValidateForSave() error = %v, wantErr %v", err, tt.wantErr) } }) } })
t.Run("CompressionRatio", func(t *testing.T) { b := &bundle.Bundle{ CompressedSize: 1000, UncompressedSize: 5000, } ratio := b.CompressionRatio() if ratio != 5.0 { t.Errorf("expected ratio 5.0, got %f", ratio) } })}
// TestMempool tests mempool operationsfunc TestMempool(t *testing.T) { tmpDir := t.TempDir() logger := &testLogger{t: t}
t.Run("CreateAndAdd", func(t *testing.T) { minTime := time.Now().Add(-time.Hour) m, err := bundle.NewMempool(tmpDir, 1, minTime, logger) if err != nil { t.Fatalf("NewMempool failed: %v", err) }
ops := makeTestOperations(100) added, err := m.Add(ops) if err != nil { t.Fatalf("Add failed: %v", err) } if added != 100 { t.Errorf("expected 100 added, got %d", added) } if m.Count() != 100 { t.Errorf("expected count 100, got %d", m.Count()) } })
t.Run("ChronologicalValidation", func(t *testing.T) { minTime := time.Now().Add(-time.Hour) m, err := bundle.NewMempool(tmpDir, 2, minTime, logger) if err != nil { t.Fatalf("NewMempool failed: %v", err) }
// Add operations in order ops := makeTestOperations(10) _, err = m.Add(ops) if err != nil { t.Fatalf("Add failed: %v", err) }
// Try to add operation before last one (should fail) oldOp := []plc.PLCOperation{ { DID: "did:plc:old", CID: "old123", CreatedAt: time.Now().Add(-2 * time.Hour), }, } _, err = m.Add(oldOp) if err == nil { t.Error("expected chronological validation error") } })
t.Run("TakeOperations", func(t *testing.T) { minTime := time.Now().Add(-time.Hour) m, err := bundle.NewMempool(tmpDir, 3, minTime, logger) if err != nil { t.Fatalf("NewMempool failed: %v", err) }
ops := makeTestOperations(100) m.Add(ops)
taken, err := m.Take(50) if err != nil { t.Fatalf("Take failed: %v", err) } if len(taken) != 50 { t.Errorf("expected 50 operations, got %d", len(taken)) } if m.Count() != 50 { t.Errorf("expected 50 remaining, got %d", m.Count()) } })
t.Run("SaveAndLoad", func(t *testing.T) { minTime := time.Now().Add(-time.Hour) m, err := bundle.NewMempool(tmpDir, 4, minTime, logger) if err != nil { t.Fatalf("NewMempool failed: %v", err) }
ops := makeTestOperations(50) m.Add(ops)
if err := m.Save(); err != nil { t.Fatalf("Save failed: %v", err) }
// Create new mempool and load m2, err := bundle.NewMempool(tmpDir, 4, minTime, logger) if err != nil { t.Fatalf("NewMempool failed: %v", err) }
if m2.Count() != 50 { t.Errorf("expected 50 operations after load, got %d", m2.Count()) } })
t.Run("Validate", func(t *testing.T) { minTime := time.Now().Add(-time.Hour) m, err := bundle.NewMempool(tmpDir, 5, minTime, logger) if err != nil { t.Fatalf("NewMempool failed: %v", err) }
ops := makeTestOperations(10) m.Add(ops)
if err := m.Validate(); err != nil { t.Errorf("Validate failed: %v", err) } })}
// TestOperations tests low-level operationsfunc TestOperations(t *testing.T) { tmpDir := t.TempDir() logger := &testLogger{t: t}
ops, err := bundle.NewOperations(logger) if err != nil { t.Fatalf("NewOperations failed: %v", err) } defer ops.Close()
t.Run("SerializeJSONL", func(t *testing.T) { operations := makeTestOperations(10) data := ops.SerializeJSONL(operations) if len(data) == 0 { t.Error("SerializeJSONL returned empty data") } })
t.Run("Hash", func(t *testing.T) { data := []byte("test data") hash := ops.Hash(data) if len(hash) != 64 { // SHA256 hex = 64 chars t.Errorf("expected hash length 64, got %d", len(hash)) }
// Same data should produce same hash hash2 := ops.Hash(data) if hash != hash2 { t.Error("same data produced different hashes") } })
t.Run("SaveAndLoadBundle", func(t *testing.T) { operations := makeTestOperations(bundle.BUNDLE_SIZE) path := filepath.Join(tmpDir, "test_bundle.jsonl.zst")
// Save uncompHash, compHash, uncompSize, compSize, err := ops.SaveBundle(path, operations) if err != nil { t.Fatalf("SaveBundle failed: %v", err) }
if uncompHash == "" || compHash == "" { t.Error("empty hashes returned") } if uncompSize == 0 || compSize == 0 { t.Error("zero sizes returned") } if compSize >= uncompSize { t.Error("compressed size should be smaller than uncompressed") }
// Load loaded, err := ops.LoadBundle(path) if err != nil { t.Fatalf("LoadBundle failed: %v", err) }
if len(loaded) != len(operations) { t.Errorf("expected %d operations, got %d", len(operations), len(loaded)) } })
t.Run("ExtractUniqueDIDs", func(t *testing.T) { operations := []plc.PLCOperation{ {DID: "did:plc:1"}, {DID: "did:plc:2"}, {DID: "did:plc:1"}, // duplicate {DID: "did:plc:3"}, }
dids := ops.ExtractUniqueDIDs(operations) if len(dids) != 3 { t.Errorf("expected 3 unique DIDs, got %d", len(dids)) } })
t.Run("GetBoundaryCIDs", func(t *testing.T) { baseTime := time.Now() operations := []plc.PLCOperation{ {CID: "cid1", CreatedAt: baseTime}, {CID: "cid2", CreatedAt: baseTime.Add(time.Second)}, {CID: "cid3", CreatedAt: baseTime.Add(2 * time.Second)}, {CID: "cid4", CreatedAt: baseTime.Add(2 * time.Second)}, // same as cid3 {CID: "cid5", CreatedAt: baseTime.Add(2 * time.Second)}, // same as cid3 }
boundaryTime, cids := ops.GetBoundaryCIDs(operations) if !boundaryTime.Equal(baseTime.Add(2 * time.Second)) { t.Error("unexpected boundary time") } if len(cids) != 3 { // cid3, cid4, cid5 t.Errorf("expected 3 boundary CIDs, got %d", len(cids)) } })}
// Helper functions
func makeTestOperations(count int) []plc.PLCOperation { ops := make([]plc.PLCOperation, count) baseTime := time.Now().Add(-time.Hour)
for i := 0; i < count; i++ { ops[i] = plc.PLCOperation{ DID: "did:plc:test" + string(rune(i)), CID: "bafytest" + string(rune(i)), CreatedAt: baseTime.Add(time.Duration(i) * time.Second), /*Operation: map[string]interface{}{ "type": "create", },*/ } }
return ops}
type testLogger struct { t *testing.T}
func (l *testLogger) Printf(format string, v ...interface{}) { l.t.Logf(format, v...)}
func (l *testLogger) Println(v ...interface{}) { l.t.Log(v...)}