Something went wrong. Try again.
An ATProto Powered Reputation Network
Something went wrong. Try again.
5.7 kB · 148 lines
Go
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149package storage
import ( "context" "database/sql" "errors" "path/filepath" "testing" "time"
"provenance/internal/domain" "provenance/internal/trust")
func TestSoftStorage(t *testing.T) { ctx := context.Background() path := filepath.Join(t.TempDir(), "soft.db") s, err := OpenWithSoft(path, domain.SoftParticipants) if err != nil { t.Fatal(err) } defer func() { s.Close() }() const author = "did:plc:a" const target = "did:plc:b" if err := s.Join(ctx, author); err != nil { t.Fatal(err) } v := domain.Vouch{Author: author, Subject: target, Source: domain.FollowCollection, RecordKey: "follow-1", Score: domain.FollowScore} apply := func(v *domain.Vouch, source, key string, stamp int64) { t.Helper() if err := s.Apply(ctx, "soft-stream", domain.Change{Kind: domain.RecordChange, DID: author, Source: source, RecordKey: key, Vouch: v, TimeUS: stamp}); err != nil { t.Fatal(err) } } apply(&v, v.Source, v.RecordKey, 100) graph := func(mode domain.Mode, tag string) []domain.Vouch { t.Helper() rows, err := s.Graph(ctx, tag, mode, 10) if err != nil { t.Fatal(err) } return trust.Select(rows, tag, mode) } if got := graph(domain.Default, ""); len(got) != 1 || got[0].Score != domain.FollowScore || got[0].Source != domain.FollowCollection { t.Fatalf("follow: %+v", got) } if got := graph(domain.Tagged, "work"); len(got) != 0 { t.Fatalf("soft topic-only edge: %+v", got) } if got := graph(domain.Fallback, "work"); len(got) != 1 { t.Fatalf("soft fallback: %+v", got) } duplicate := v duplicate.RecordKey = "follow-2" apply(&duplicate, duplicate.Source, duplicate.RecordKey, 110) if got := graph(domain.Default, ""); len(got) != 1 || got[0].RecordKey != v.RecordKey { t.Fatalf("duplicate follows: %+v", got) } block := v block.Source, block.Score = domain.BlockCollection, domain.SoftBlockScore apply(&block, block.Source, block.RecordKey, 120) if got := graph(domain.Default, ""); len(got) != 1 || got[0].Score != domain.SoftBlockScore { t.Fatalf("block precedence: %+v", got) } explicit := domain.Vouch{Author: author, Subject: target, Score: 0} apply(&explicit, domain.Collection, domain.RecordKey(target, ""), 130) if got := graph(domain.Default, ""); len(got) != 1 || got[0].Score != 0 || got[0].Source != domain.Collection { t.Fatalf("explicit zero lost: %+v", got) } apply(nil, domain.Collection, domain.RecordKey(target, ""), 140) apply(nil, block.Source, block.RecordKey, 150) apply(&block, block.Source, block.RecordKey, 120) if got := graph(domain.Default, ""); len(got) != 1 || got[0].Score != domain.FollowScore { t.Fatalf("tombstone replay: %+v", got) } rows, revision, err := s.TrustGraph(ctx, "", domain.Default, 10) if err != nil || len(rows) != 1 { t.Fatal(err) } result := trust.Result{Status: trust.CalculatedStatus, Score: &v.Score} deps := []trust.Dependency{{Author: author, Source: v.Source, RecordKey: v.RecordKey}} now := time.UnixMicro(1000) if saved, err := s.SaveTrust(ctx, "follow-cache", result, deps, revision, now.Add(time.Hour)); err != nil || !saved { t.Fatalf("save: %v %v", saved, err) } // The same record key in another collection must not invalidate this path. apply(nil, domain.BlockCollection, v.RecordKey, 160) if _, err := s.CachedTrust(ctx, "follow-cache", now); err != nil { t.Fatalf("source collision: %v", err) } apply(nil, v.Source, v.RecordKey, 170) if _, err := s.CachedTrust(ctx, "follow-cache", now); !errors.Is(err, sql.ErrNoRows) { t.Fatalf("soft path not invalidated: %v", err) } if got := graph(domain.Default, ""); len(got) != 1 || got[0].RecordKey != duplicate.RecordKey { t.Fatalf("remaining duplicate: %+v", got) } if err := s.Replace(ctx, author, []domain.Vouch{v, block}, time.UnixMicro(200)); err != nil { t.Fatal(err) } apply(nil, block.Source, block.RecordKey, 190) if got := graph(domain.Default, ""); len(got) != 1 || got[0].Score != domain.SoftBlockScore { t.Fatalf("snapshot watermark: %+v", got) } if err := s.Apply(ctx, "soft-stream", domain.Change{Kind: domain.AccountChange, DID: author, TimeUS: 210, Active: false, Status: domain.DeletedAccount}); err != nil { t.Fatal(err) } if err := s.Apply(ctx, "soft-stream", domain.Change{Kind: domain.AccountChange, DID: author, TimeUS: 220, Active: true}); err != nil { t.Fatal(err) } apply(&v, v.Source, v.RecordKey, 205) if got := graph(domain.Default, ""); len(got) != 0 { t.Fatalf("deleted account replay: %+v", got) }}
func TestSoftAuthorScope(t *testing.T) { ctx := context.Background() for _, mode := range []domain.SoftMode{domain.SoftDisabled, domain.SoftParticipants, domain.SoftAllAuthors} { s, err := OpenWithSoft(filepath.Join(t.TempDir(), "scope.db"), mode) if err != nil { t.Fatal(err) } v := domain.Vouch{Author: "did:plc:observer", Subject: "did:plc:target", Source: domain.FollowCollection, RecordKey: "follow", Score: domain.FollowScore} if err := s.Apply(ctx, "stream", domain.Change{Kind: domain.RecordChange, DID: v.Author, Source: v.Source, RecordKey: v.RecordKey, Vouch: &v, TimeUS: 100}); err != nil { t.Fatal(err) } rows, err := s.Incoming(ctx, v.Subject, "", domain.Default, 10) if err != nil { t.Fatal(err) } want := 0 if mode == domain.SoftAllAuthors { want = 1 } if len(rows) != want { t.Fatalf("scope %v: %+v", mode, rows) } if _, err := s.Participant(ctx, v.Author); !errors.Is(err, sql.ErrNoRows) { t.Fatalf("soft author enrolled: %v", err) } var count int if err := s.conn.QueryRow("SELECT count(*) FROM soft_vouches").Scan(&count); err != nil || count != want { t.Fatalf("disabled indexing: %d %v", count, err) } s.Close() }}