Artifact 948d2b64cf7db28e7beb0a23875021f04657898fb9e0c9ef27c1fbfcfc33412c:
- File
internal/sync/cluster_integration_test.go
— part of check-in
[f5744359d3]
at
2026-04-19 16:30:54
on branch trunk
— Initial open source release
libfossil is a pure-Go implementation of the Fossil SCM, reading and writing the same .fossil SQLite repository format as upstream Fossil.
This is the first public release of code that previously lived in a private repository. The module path is github.com/danmestas/libfossil and the binary is named libfossil to avoid $PATH collisions with upstream fossil.
Included: - Repo lifecycle (create, clone), checkout/checkin, timeline, merge, diff, annotate, shun, tag, branch, bisect, stash, undo/redo - Sync-over-HTTP protocol (client and handler) - Two SQLite drivers: modernc (pure Go, default) and ncruces (wasm-capable) - Observer interfaces (SyncObserver, CheckoutObserver) with an optional OpenTelemetry implementation in observer/otel (separate submodule) - Deterministic simulation testing harness with BUGGIFY fault injection - Kong-based embeddable CLI under cli/ and a standalone binary under cmd/libfossil/ (user: [email protected], size: 2675) [annotate] [blame] [check-ins using]
package sync import ( "context" "fmt" "path/filepath" "testing" "github.com/danmestas/libfossil/internal/blob" "github.com/danmestas/libfossil/internal/repo" "github.com/danmestas/libfossil/simio" "github.com/danmestas/libfossil/internal/xfer" _ "github.com/danmestas/libfossil/internal/testdriver" ) // TestClusterSync_RoundTrip creates a client repo with 200 blobs, syncs them // to an empty server repo via MockTransport wrapping HandleSync, and verifies // all 200 blobs arrive within 10 rounds. func TestClusterSync_RoundTrip(t *testing.T) { // --- Setup: client repo with 200 blobs --- clientPath := filepath.Join(t.TempDir(), "client.fossil") clientRepo, err := repo.Create(clientPath, "testuser", simio.CryptoRand{}) if err != nil { t.Fatalf("repo.Create client: %v", err) } t.Cleanup(func() { clientRepo.Close() }) for i := range 200 { content := []byte(fmt.Sprintf("cluster-roundtrip-blob-%04d", i)) _, _, err := blob.Store(clientRepo.DB(), content) if err != nil { t.Fatalf("blob.Store[%d]: %v", i, err) } } // --- Setup: empty server repo --- serverPath := filepath.Join(t.TempDir(), "server.fossil") serverRepo, err := repo.Create(serverPath, "testuser", simio.CryptoRand{}) if err != nil { t.Fatalf("repo.Create server: %v", err) } t.Cleanup(func() { serverRepo.Close() }) // --- Transport: MockTransport wrapping HandleSync --- transport := &MockTransport{ Handler: func(req *xfer.Message) *xfer.Message { resp, err := HandleSync(context.Background(), serverRepo, req) if err != nil { t.Fatalf("HandleSync: %v", err) } return resp }, } // --- Sync: client pushes to server --- var projCode, srvCode string clientRepo.DB().QueryRow("SELECT value FROM config WHERE name='project-code'").Scan(&projCode) clientRepo.DB().QueryRow("SELECT value FROM config WHERE name='server-code'").Scan(&srvCode) result, err := Sync(context.Background(), clientRepo, transport, SyncOpts{ Push: true, Pull: true, ProjectCode: projCode, ServerCode: srvCode, }) if err != nil { t.Fatalf("Sync: %v", err) } // --- Verify: all 200 blobs arrived --- var serverCount int err = serverRepo.DB().QueryRow("SELECT count(*) FROM blob WHERE size >= 0").Scan(&serverCount) if err != nil { t.Fatalf("count blobs: %v", err) } t.Logf("sync completed: rounds=%d sent=%d recv=%d server_blobs=%d", result.Rounds, result.FilesSent, result.FilesRecvd, serverCount) if serverCount < 200 { t.Fatalf("server has %d blobs, want >= 200", serverCount) } // --- Verify: converged within 10 rounds --- if result.Rounds > 10 { t.Fatalf("took %d rounds, want <= 10", result.Rounds) } }