From 59c4aecaf055781e1542d46e05f92e0f113d0830 Mon Sep 17 00:00:00 2001 From: Marcin Czenko Date: Wed, 22 Oct 2025 21:56:45 +0200 Subject: [PATCH] comparing testify with native go testing --- .../codex_archive_downloader_testify_test.go | 296 ++++++++++++++++++ go.mod | 12 +- go.sum | 10 + 3 files changed, 316 insertions(+), 2 deletions(-) create mode 100644 communities/codex_archive_downloader_testify_test.go diff --git a/communities/codex_archive_downloader_testify_test.go b/communities/codex_archive_downloader_testify_test.go new file mode 100644 index 0000000..b23da8c --- /dev/null +++ b/communities/codex_archive_downloader_testify_test.go @@ -0,0 +1,296 @@ +//go:build !disable_torrent +// +build !disable_torrent + +package communities_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "go-codex-client/communities" + "go-codex-client/protobuf" + mock_communities "go-codex-client/communities/mock" + + "go.uber.org/mock/gomock" +) + +// CodexArchiveDownloaderTestifySuite demonstrates testify's suite functionality +type CodexArchiveDownloaderTestifySuite struct { + suite.Suite + ctrl *gomock.Controller + mockClient *mock_communities.MockCodexClientInterface + index *protobuf.CodexWakuMessageArchiveIndex +} + +// SetupTest runs before each test method +func (suite *CodexArchiveDownloaderTestifySuite) SetupTest() { + suite.ctrl = gomock.NewController(suite.T()) + suite.mockClient = mock_communities.NewMockCodexClientInterface(suite.ctrl) + suite.index = &protobuf.CodexWakuMessageArchiveIndex{ + Archives: map[string]*protobuf.CodexWakuMessageArchiveIndexMetadata{ + "test-archive-hash-1": { + Cid: "test-cid-1", + Metadata: &protobuf.WakuMessageArchiveMetadata{ + From: 1000, + To: 2000, + }, + }, + }, + } +} + +// TearDownTest runs after each test method +func (suite *CodexArchiveDownloaderTestifySuite) TearDownTest() { + suite.ctrl.Finish() +} + +// TestBasicSingleArchive demonstrates testify assertions vs standard library +func (suite *CodexArchiveDownloaderTestifySuite) TestBasicSingleArchive() { + // Test data + communityID := "test-community" + existingArchiveIDs := []string{} // No existing archives + cancelChan := make(chan struct{}) + + // Set up mock expectations - same as before + suite.mockClient.EXPECT(). + TriggerDownloadWithContext(gomock.Any(), "test-cid-1"). + Return(&communities.CodexManifest{CID: "test-cid-1"}, nil). + Times(1) + + // First HasCid call returns false, second returns true (simulating polling) + gomock.InOrder( + suite.mockClient.EXPECT().HasCid("test-cid-1").Return(false, nil), + suite.mockClient.EXPECT().HasCid("test-cid-1").Return(true, nil), + ) + + // Create downloader with mock client + downloader := communities.NewCodexArchiveDownloader(suite.mockClient, suite.index, communityID, existingArchiveIDs, cancelChan) + + // Set fast polling interval for tests (10ms instead of default 1s) + downloader.SetPollingInterval(10 * time.Millisecond) + + // Set up callback to track completion + var callbackInvoked bool + var callbackHash string + var callbackFrom, callbackTo uint64 + + downloader.SetOnArchiveDownloaded(func(hash string, from, to uint64) { + callbackInvoked = true + callbackHash = hash + callbackFrom = from + callbackTo = to + }) + + // Verify initial state - compare testify vs standard assertions + // Testify version is more readable and provides better error messages + assert.Equal(suite.T(), 1, downloader.GetTotalArchivesCount(), "Total archives count should be 1") + assert.Equal(suite.T(), 0, downloader.GetTotalDownloadedArchivesCount(), "Downloaded archives should be 0 initially") + assert.False(suite.T(), downloader.IsDownloadComplete(), "Download should not be complete initially") + + // Start the download + downloader.StartDownload() + + // Wait for download to complete (with timeout) + require.Eventually(suite.T(), func() bool { + return downloader.IsDownloadComplete() + }, 5*time.Second, 100*time.Millisecond, "Download should complete within 5 seconds") + + // Verify final state - testify makes these assertions more expressive + assert.True(suite.T(), downloader.IsDownloadComplete(), "Download should be complete") + assert.Equal(suite.T(), 1, downloader.GetTotalDownloadedArchivesCount(), "Should have 1 downloaded archive") + + // Verify callback was invoked - multiple related assertions grouped logically + assert.True(suite.T(), callbackInvoked, "Callback should be invoked") + assert.Equal(suite.T(), "test-archive-hash-1", callbackHash, "Callback hash should match expected") + assert.Equal(suite.T(), uint64(1000), callbackFrom, "Callback from should be 1000") + assert.Equal(suite.T(), uint64(2000), callbackTo, "Callback to should be 2000") + + suite.T().Log("✅ Basic single archive download test passed (testify version)") + suite.T().Log(" - All mock expectations satisfied") + suite.T().Logf(" - Callback invoked: %v", callbackInvoked) +} + +// TestBasicSingleArchiveFunction demonstrates testify without test suite (function-based) +func TestCodexArchiveDownloader_BasicSingleArchive_Testify(t *testing.T) { + // Create gomock controller + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Create test data using testify helper + index := createTestIndexTestify() + communityID := "test-community" + existingArchiveIDs := []string{} // No existing archives + cancelChan := make(chan struct{}) + + // Create mock client + mockClient := mock_communities.NewMockCodexClientInterface(ctrl) + + // Set up expectations + mockClient.EXPECT(). + TriggerDownloadWithContext(gomock.Any(), "test-cid-1"). + Return(&communities.CodexManifest{CID: "test-cid-1"}, nil). + Times(1) + + // First HasCid call returns false, second returns true (simulating polling) + gomock.InOrder( + mockClient.EXPECT().HasCid("test-cid-1").Return(false, nil), + mockClient.EXPECT().HasCid("test-cid-1").Return(true, nil), + ) + + // Create downloader with mock client + downloader := communities.NewCodexArchiveDownloader(mockClient, index, communityID, existingArchiveIDs, cancelChan) + + // Set fast polling interval for tests (10ms instead of default 1s) + downloader.SetPollingInterval(10 * time.Millisecond) + + // Set up callback to track completion + var callbackData struct { + invoked bool + hash string + from uint64 + to uint64 + } + + downloader.SetOnArchiveDownloaded(func(hash string, from, to uint64) { + callbackData.invoked = true + callbackData.hash = hash + callbackData.from = from + callbackData.to = to + }) + + // Verify initial state using testify assertions + // Notice how readable and self-documenting these are compared to if/t.Error patterns + require.Equal(t, 1, downloader.GetTotalArchivesCount()) + require.Equal(t, 0, downloader.GetTotalDownloadedArchivesCount()) + require.False(t, downloader.IsDownloadComplete()) + + // Start the download + downloader.StartDownload() + + // Wait for download to complete - testify's Eventually is much cleaner than manual loops + require.Eventually(t, func() bool { + return downloader.IsDownloadComplete() + }, 5*time.Second, 100*time.Millisecond) + + // Verify final state + assert.True(t, downloader.IsDownloadComplete()) + assert.Equal(t, 1, downloader.GetTotalDownloadedArchivesCount()) + + // Verify callback - testify allows for compound assertions + require.True(t, callbackData.invoked, "Callback should be invoked") + + // Group related assertions for better test organization + assert.Equal(t, "test-archive-hash-1", callbackData.hash) + assert.Equal(t, uint64(1000), callbackData.from) + assert.Equal(t, uint64(2000), callbackData.to) + + t.Log("✅ Function-based testify test passed") +} + +// TestMultipleArchivesWithTestify demonstrates testify's advantages for complex scenarios +func TestCodexArchiveDownloader_MultipleArchives_Testify(t *testing.T) { + // Create gomock controller + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Create test data with multiple archives + index := &protobuf.CodexWakuMessageArchiveIndex{ + Archives: map[string]*protobuf.CodexWakuMessageArchiveIndexMetadata{ + "archive-1": { + Cid: "cid-1", + Metadata: &protobuf.WakuMessageArchiveMetadata{From: 1000, To: 2000}, + }, + "archive-2": { + Cid: "cid-2", + Metadata: &protobuf.WakuMessageArchiveMetadata{From: 2000, To: 3000}, + }, + "archive-3": { + Cid: "cid-3", + Metadata: &protobuf.WakuMessageArchiveMetadata{From: 3000, To: 4000}, + }, + }, + } + + communityID := "test-community" + existingArchiveIDs := []string{} // No existing archives + cancelChan := make(chan struct{}) + + // Create mock client + mockClient := mock_communities.NewMockCodexClientInterface(ctrl) + + // Set up expectations for all 3 archives - testify makes verification cleaner + expectedCids := []string{"cid-1", "cid-2", "cid-3"} + + for _, cid := range expectedCids { + mockClient.EXPECT(). + TriggerDownloadWithContext(gomock.Any(), cid). + Return(&communities.CodexManifest{CID: cid}, nil). + Times(1) + + // Each archive becomes available after one poll + gomock.InOrder( + mockClient.EXPECT().HasCid(cid).Return(false, nil), + mockClient.EXPECT().HasCid(cid).Return(true, nil), + ) + } + + // Create downloader + downloader := communities.NewCodexArchiveDownloader(mockClient, index, communityID, existingArchiveIDs, cancelChan) + downloader.SetPollingInterval(10 * time.Millisecond) + + // Track completed archives + completedArchives := make(map[string]bool) + downloader.SetOnArchiveDownloaded(func(hash string, from, to uint64) { + completedArchives[hash] = true + }) + + // Initial state verification + assert.Equal(t, 3, downloader.GetTotalArchivesCount(), "Should have 3 total archives") + assert.Equal(t, 0, downloader.GetTotalDownloadedArchivesCount(), "Should start with 0 downloaded") + assert.False(t, downloader.IsDownloadComplete(), "Should not be complete initially") + + // Start download + downloader.StartDownload() + + // Wait for all downloads to complete + require.Eventually(t, func() bool { + return downloader.IsDownloadComplete() + }, 10*time.Second, 100*time.Millisecond, "All downloads should complete within 10 seconds") + + // Final state verification - testify makes these checks very readable + assert.True(t, downloader.IsDownloadComplete(), "Download should be complete") + assert.Equal(t, 3, downloader.GetTotalDownloadedArchivesCount(), "Should have downloaded all 3 archives") + + // Verify all archives were processed + assert.Len(t, completedArchives, 3, "Should have completed exactly 3 archives") + assert.Contains(t, completedArchives, "archive-1", "Should have completed archive-1") + assert.Contains(t, completedArchives, "archive-2", "Should have completed archive-2") + assert.Contains(t, completedArchives, "archive-3", "Should have completed archive-3") + + t.Log("✅ Multiple archives testify test passed") + t.Logf(" - Completed %d out of %d archives", len(completedArchives), 3) +} + +// Helper function for testify tests +func createTestIndexTestify() *protobuf.CodexWakuMessageArchiveIndex { + return &protobuf.CodexWakuMessageArchiveIndex{ + Archives: map[string]*protobuf.CodexWakuMessageArchiveIndexMetadata{ + "test-archive-hash-1": { + Cid: "test-cid-1", + Metadata: &protobuf.WakuMessageArchiveMetadata{ + From: 1000, + To: 2000, + }, + }, + }, + } +} + +// Run the test suite +func TestCodexArchiveDownloaderSuite(t *testing.T) { + suite.Run(t, new(CodexArchiveDownloaderTestifySuite)) +} \ No newline at end of file diff --git a/go.mod b/go.mod index ed40209..458a6cf 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,14 @@ module go-codex-client go 1.23.0 -require google.golang.org/protobuf v1.34.1 +require ( + github.com/stretchr/testify v1.11.1 + go.uber.org/mock v0.6.0 + google.golang.org/protobuf v1.34.1 +) -require go.uber.org/mock v0.6.0 // indirect +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index b693b12..5a684eb 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,18 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=