diff --git a/e2e/cross_node_test.go b/e2e/cross_node_test.go index bab108c..985a468 100644 --- a/e2e/cross_node_test.go +++ b/e2e/cross_node_test.go @@ -53,8 +53,6 @@ func TestPublishWithoutSubscriptionOnDifferentNode(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - t.Log(tt.description) - var cancel context.CancelFunc var subCmd *exec.Cmd @@ -65,8 +63,6 @@ func TestPublishWithoutSubscriptionOnDifferentNode(t *testing.T) { defer cancel() subProxy := proxies[tt.subscribeOn] - t.Logf("Starting subscriber on proxy %d: %s", tt.subscribeOn, subProxy) - subCmd = exec.CommandContext(ctx, cliBinaryPath, "subscribe", "--topic="+testTopic, @@ -77,14 +73,10 @@ func TestPublishWithoutSubscriptionOnDifferentNode(t *testing.T) { // Wait for subscriber to be active time.Sleep(3 * time.Second) - t.Log("Subscriber active") - } else { - t.Log("No subscriber started (testing publish without subscription)") } // Attempt to publish pubProxy := proxies[tt.publishOn] - t.Logf("Publishing on proxy %d: %s", tt.publishOn, pubProxy) out, err := RunCommand(cliBinaryPath, "publish", "--topic="+testTopic, @@ -94,17 +86,12 @@ func TestPublishWithoutSubscriptionOnDifferentNode(t *testing.T) { // Validate expectations if tt.shouldFail { // Should fail with "topic not assigned" or similar - if err == nil { - t.Errorf("CRITICAL: Publishing without subscriber should have failed but succeeded! Output: %s", out) - } else { - lowerOut := strings.ToLower(out) - if strings.Contains(lowerOut, "topic not assigned") || - strings.Contains(lowerOut, "not found") || - strings.Contains(lowerOut, "failed") { - t.Logf("✅ Correctly rejected: %s", out) - } else { - t.Logf("⚠️ Failed but with unexpected error: %s", out) - } + require.Error(t, err, "Publishing without subscriber should have failed. Output: %s", out) + lowerOut := strings.ToLower(out) + if !strings.Contains(lowerOut, "topic not assigned") && + !strings.Contains(lowerOut, "not found") && + !strings.Contains(lowerOut, "failed") { + t.Logf("Unexpected error message: %s", out) } } else { // Should succeed @@ -113,8 +100,6 @@ func TestPublishWithoutSubscriptionOnDifferentNode(t *testing.T) { validator := NewValidator(out) err := validator.ValidatePublishSuccess() require.NoError(t, err, "Publish validation failed") - - t.Logf("✅ Cross-node publish succeeded") } // Cleanup @@ -140,8 +125,6 @@ func TestCrossProxyFailover(t *testing.T) { // Test with a definitely invalid proxy invalidProxy := "http://192.0.2.1:8080" // TEST-NET-1 (non-routable) - t.Log("Testing publish to unreachable proxy (should fail quickly)") - // Start subscriber on valid proxy first validProxy := GetDefaultProxy() @@ -173,8 +156,6 @@ func TestCrossProxyFailover(t *testing.T) { require.Less(t, duration.Seconds(), 35.0, "Publish to unreachable proxy should timeout/fail within 35 seconds, took %v", duration) - t.Logf("✅ Correctly failed to publish to unreachable proxy in %v", duration) - cancel() subCmd.Wait() } @@ -188,8 +169,6 @@ func TestMultipleSubscribersOnDifferentProxies(t *testing.T) { testTopic := fmt.Sprintf("multi-sub-%d", time.Now().Unix()) testMessage := fmt.Sprintf("MultiSubTest-%d", time.Now().Unix()) - t.Log("Starting subscribers on multiple proxies...") - // Start subscribers on both proxies ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -204,13 +183,10 @@ func TestMultipleSubscribersOnDifferentProxies(t *testing.T) { err := subCmd.Start() require.NoError(t, err, "Failed to start subscriber %d on %s", i, proxy) subCmds = append(subCmds, subCmd) - - t.Logf("Subscriber %d started on %s", i, proxy) } // Wait for all subscribers to be ready time.Sleep(4 * time.Second) - t.Log("All subscribers ready") // Publish message to first proxy out, err := RunCommand(cliBinaryPath, "publish", @@ -224,8 +200,6 @@ func TestMultipleSubscribersOnDifferentProxies(t *testing.T) { err = validator.ValidatePublishSuccess() require.NoError(t, err) - t.Log("✅ Successfully published to topic with multiple cross-proxy subscribers") - // Cleanup cancel() for _, cmd := range subCmds { @@ -252,8 +226,7 @@ func TestProxyHealthBeforePublish(t *testing.T) { validator := NewValidator(healthOut) healthInfo, err := validator.ValidateHealthCheck() require.NoError(t, err, "Health check validation failed") - - t.Logf("Proxy %s health: %s", proxy, healthInfo.Status) + require.Equal(t, "ok", healthInfo.Status, "Proxy %s should be healthy", proxy) // If healthy, test should be able to publish (with subscriber) testTopic := fmt.Sprintf("health-pub-%d-%d", i, time.Now().Unix()) @@ -280,7 +253,6 @@ func TestProxyHealthBeforePublish(t *testing.T) { subCmd.Wait() require.NoError(t, pubErr, "Proxy reported healthy but publish failed: %s", pubOut) - t.Logf("✅ Healthy proxy successfully processed publish") }) } } diff --git a/e2e/integration_test.go b/e2e/integration_test.go index f00f779..f10ed21 100644 --- a/e2e/integration_test.go +++ b/e2e/integration_test.go @@ -40,7 +40,6 @@ func TestFullWorkflow(t *testing.T) { }) // Start subscriber in background before publishing - t.Log("Starting background subscriber for workflow tests...") ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -51,7 +50,6 @@ func TestFullWorkflow(t *testing.T) { // Wait for subscription to be active time.Sleep(2 * time.Second) - t.Log("Subscriber active, proceeding with publish tests...") t.Run("4_publish_http_message", func(t *testing.T) { out, err := RunCommand(cliBinaryPath, "publish", @@ -99,7 +97,6 @@ func TestFullWorkflow(t *testing.T) { // Cleanup: stop subscriber cancel() subCmd.Wait() - t.Log("Background subscriber stopped") } func TestCrossProxyWorkflow(t *testing.T) { @@ -111,7 +108,6 @@ func TestCrossProxyWorkflow(t *testing.T) { testTopic := fmt.Sprintf("cross-proxy-%d", time.Now().Unix()) // Start subscriber on first proxy - t.Log("Starting background subscriber for cross-proxy tests...") ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -122,7 +118,6 @@ func TestCrossProxyWorkflow(t *testing.T) { // Wait for subscription to be active time.Sleep(2 * time.Second) - t.Log("Subscriber active on first proxy...") for i, proxy := range proxies { proxyName := fmt.Sprintf("proxy_%d", i+1) @@ -145,5 +140,4 @@ func TestCrossProxyWorkflow(t *testing.T) { // Cleanup: stop subscriber cancel() subCmd.Wait() - t.Log("Background subscriber stopped") } diff --git a/e2e/publish_test.go b/e2e/publish_test.go index e205c1e..bd84f32 100644 --- a/e2e/publish_test.go +++ b/e2e/publish_test.go @@ -5,6 +5,8 @@ import ( "fmt" "os" "os/exec" + "path/filepath" + "strings" "testing" "time" @@ -19,7 +21,6 @@ func TestPublishCommand(t *testing.T) { testTopic := fmt.Sprintf("test-publish-%d", time.Now().Unix()) // Start a subscriber in the background to enable publishing - t.Log("Starting background subscriber to enable publishing...") ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -29,7 +30,6 @@ func TestPublishCommand(t *testing.T) { // Wait for subscription to be active time.Sleep(2 * time.Second) - t.Log("Subscriber active, proceeding with publish tests...") tests := []struct { name string @@ -81,6 +81,7 @@ func TestPublishCommand(t *testing.T) { }, } + // Run the basic tests first for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { out, err := RunCommand(cliBinaryPath, tt.args...) @@ -98,8 +99,71 @@ func TestPublishCommand(t *testing.T) { }) } + // Test --file flag scenarios + t.Run("publish from file HTTP", func(t *testing.T) { + dir := t.TempDir() + testFile := filepath.Join(dir, "test-publish.txt") + testContent := "Test file content for HTTP publish" + err := os.WriteFile(testFile, []byte(testContent), 0644) + require.NoError(t, err, "Failed to create test file") + + out, err := RunCommand(cliBinaryPath, "publish", + "--topic="+testTopic, + "--file="+testFile, + "--service-url="+serviceURL) + require.NoError(t, err, "File publish failed: %v\nOutput: %s", err, out) + + validator := NewValidator(out) + err = validator.ValidatePublishSuccess() + require.NoError(t, err, "File publish validation failed") + }) + + t.Run("publish from file gRPC", func(t *testing.T) { + dir := t.TempDir() + testFile := filepath.Join(dir, "test-publish-grpc.txt") + testContent := "Test file content for gRPC publish" + err := os.WriteFile(testFile, []byte(testContent), 0644) + require.NoError(t, err, "Failed to create test file") + + out, err := RunCommand(cliBinaryPath, "publish", + "--topic="+testTopic, + "--file="+testFile, + "--grpc", + "--service-url="+serviceURL) + require.NoError(t, err, "File gRPC publish failed: %v\nOutput: %s", err, out) + + validator := NewValidator(out) + err = validator.ValidatePublishSuccess() + require.NoError(t, err, "File gRPC publish validation failed") + }) + + t.Run("publish file not found", func(t *testing.T) { + dir := t.TempDir() + nonExistentFile := filepath.Join(dir, "nonexistent-file.txt") + out, err := RunCommand(cliBinaryPath, "publish", + "--topic="+testTopic, + "--file="+nonExistentFile, + "--service-url="+serviceURL) + require.Error(t, err, "Expected file not found error. Output: %s", out) + require.Contains(t, strings.ToLower(out), "failed to read file", "Expected file read error") + }) + + t.Run("publish file and message both (should fail)", func(t *testing.T) { + dir := t.TempDir() + testFile := filepath.Join(dir, "test-publish-both.txt") + err := os.WriteFile(testFile, []byte("test"), 0644) + require.NoError(t, err, "Failed to create test file") + + out, err := RunCommand(cliBinaryPath, "publish", + "--topic="+testTopic, + "--file="+testFile, + "--message=test", + "--service-url="+serviceURL) + require.Error(t, err, "Expected error when both --file and --message are provided. Output: %s", out) + require.Contains(t, strings.ToLower(out), "only one", "Expected error about using only one option") + }) + // Cleanup: stop subscriber cancel() subCmd.Wait() - t.Log("Background subscriber stopped") } diff --git a/e2e/ratelimit_scenarios_test.go b/e2e/ratelimit_scenarios_test.go new file mode 100644 index 0000000..112b2df --- /dev/null +++ b/e2e/ratelimit_scenarios_test.go @@ -0,0 +1,305 @@ +package main + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +// getInitialPublishCount is a helper function to get the initial publish count from usage stats +func getInitialPublishCount(t *testing.T) int { + t.Helper() + usageBefore, err := RunCommand(cliBinaryPath, "usage") + require.NoError(t, err, "Failed to get initial usage stats") + + validatorBefore := NewValidator(usageBefore) + usageInfoBefore, err := validatorBefore.ValidateUsage() + require.NoError(t, err, "Failed to parse initial usage stats") + + return parsePublishCount(t, usageInfoBefore.PublishCount) +} + +// getMaxMessageSize gets the MaxMessageSize limit from whoami command output +func getMaxMessageSize(t *testing.T) int64 { + t.Helper() + whoamiOut, err := RunCommand(cliBinaryPath, "whoami") + require.NoError(t, err, "Failed to get whoami output") + + // Parse "Max Message Size: X.XX MB" from table format + // Format: "Max Message Size: 2.00 MB" + pattern := `Max Message Size:\s+([\d.]+)\s+MB` + validator := NewValidator(whoamiOut) + sizeMBStr, err := validator.ExtractMatch(pattern) + require.NoError(t, err, "Failed to extract Max Message Size from whoami output: %s", whoamiOut) + + sizeMB, err := strconv.ParseFloat(sizeMBStr, 64) + require.NoError(t, err, "Failed to parse Max Message Size as float: %s", sizeMBStr) + + // Convert MB to bytes + return int64(sizeMB * 1024 * 1024) +} + +// TestRateLimiterScenarios validates that usage stats change correctly after publishing messages +func TestRateLimiterScenarios(t *testing.T) { + require.NotEmpty(t, cliBinaryPath, "CLI binary path must be set by TestMain") + + serviceURL := GetDefaultProxy() + testTopic := fmt.Sprintf("ratelimit-%d", time.Now().Unix()) + + beforeCount := getInitialPublishCount(t) + + // Start subscriber + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + subCmd := exec.CommandContext(ctx, cliBinaryPath, "subscribe", "--topic="+testTopic, "--service-url="+serviceURL) + subCmd.Env = os.Environ() + err := subCmd.Start() + require.NoError(t, err, "Failed to start subscriber") + time.Sleep(2 * time.Second) + + // Publish multiple messages + numMessages := 3 + for i := 0; i < numMessages; i++ { + msg := fmt.Sprintf("RateLimitTest-%d", i+1) + out, err := RunCommand(cliBinaryPath, "publish", + "--topic="+testTopic, + "--message="+msg, + "--service-url="+serviceURL) + require.NoError(t, err, "Publish %d failed: %s", i+1, out) + time.Sleep(500 * time.Millisecond) // Small delay between publishes + } + + cancel() + subCmd.Wait() + time.Sleep(1 * time.Second) + + // Get usage stats after publishing + usageAfter, err := RunCommand(cliBinaryPath, "usage") + require.NoError(t, err, "Failed to get usage stats after publishing") + + validatorAfter := NewValidator(usageAfter) + usageInfoAfter, err := validatorAfter.ValidateUsage() + require.NoError(t, err, "Failed to parse usage stats after publishing") + + afterCount := parsePublishCount(t, usageInfoAfter.PublishCount) + + // Verify publish count increased exactly by numMessages (tests not run in parallel) + require.Equal(t, beforeCount+numMessages, afterCount, + "Publish count should increase by exactly %d (before: %d, after: %d)", + numMessages, beforeCount, afterCount) + + // Verify data usage is present + require.Contains(t, usageAfter, "Data Used:", "Usage stats should show data usage") +} + +// TestRateLimitExceededPerHour tests that per-hour rate limit is enforced +func TestRateLimitExceededPerHour(t *testing.T) { + require.NotEmpty(t, cliBinaryPath, "CLI binary path must be set by TestMain") + + serviceURL := GetDefaultProxy() + testTopic := fmt.Sprintf("ratelimit-hour-%d", time.Now().Unix()) + + // Get initial usage stats to determine per-hour limit + usageBefore, err := RunCommand(cliBinaryPath, "usage") + require.NoError(t, err, "Failed to get initial usage stats") + + validatorBefore := NewValidator(usageBefore) + usageInfoBefore, err := validatorBefore.ValidateUsage() + require.NoError(t, err, "Failed to parse initial usage stats") + + limitPerHour, err := strconv.Atoi(usageInfoBefore.PublishLimitPerHour) + require.NoError(t, err, "Failed to parse per-hour limit") + require.Greater(t, limitPerHour, 0, "Per-hour limit should be greater than 0") + + // Get current publish count + currentCount := parsePublishCount(t, usageInfoBefore.PublishCount) + + // Calculate how many more publishes we can do before hitting the limit + remaining := limitPerHour - currentCount + if remaining <= 0 { + t.Skipf("Already at or over per-hour limit (%d/%d). Cannot test limit enforcement.", currentCount, limitPerHour) + } + // Skip if remaining is too high to avoid long test times (e.g., > 100) + if remaining > 100 { + t.Skipf("Per-hour limit is too high (%d remaining). Skipping to avoid long test times.", remaining) + } + + // Start subscriber + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + subCmd := exec.CommandContext(ctx, cliBinaryPath, "subscribe", "--topic="+testTopic, "--service-url="+serviceURL) + subCmd.Env = os.Environ() + err = subCmd.Start() + require.NoError(t, err, "Failed to start subscriber") + time.Sleep(2 * time.Second) + + // Publish up to the limit (should succeed) + for i := 0; i < remaining; i++ { + msg := fmt.Sprintf("RateLimitHourTest-%d", i+1) + out, err := RunCommand(cliBinaryPath, "publish", + "--topic="+testTopic, + "--message="+msg, + "--service-url="+serviceURL) + require.NoError(t, err, "Publish %d should succeed: %s", i+1, out) + } + + // Try to publish one more (should exceed per-hour limit) + msg := fmt.Sprintf("RateLimitHourTest-%d", remaining+1) + out, err := RunCommand(cliBinaryPath, "publish", + "--topic="+testTopic, + "--message="+msg, + "--service-url="+serviceURL) + require.Error(t, err, "Publish should fail when exceeding per-hour limit. Output: %s", out) + require.Contains(t, strings.ToLower(out), "per-hour", "Error should mention per-hour limit. Got: %s", out) + + cancel() + subCmd.Wait() +} + +// TestRateLimitExceededMessageSize tests that message size limit is enforced +func TestRateLimitExceededMessageSize(t *testing.T) { + require.NotEmpty(t, cliBinaryPath, "CLI binary path must be set by TestMain") + + serviceURL := GetDefaultProxy() + testTopic := fmt.Sprintf("ratelimit-size-%d", time.Now().Unix()) + + // Start subscriber + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + subCmd := exec.CommandContext(ctx, cliBinaryPath, "subscribe", "--topic="+testTopic, "--service-url="+serviceURL) + subCmd.Env = os.Environ() + err := subCmd.Start() + require.NoError(t, err, "Failed to start subscriber") + time.Sleep(2 * time.Second) + + // Get the actual MaxMessageSize limit from the token + maxMessageSize := getMaxMessageSize(t) + require.Greater(t, maxMessageSize, int64(0), "MaxMessageSize should be greater than 0") + + // Create a file with content that exceeds the limit by 1 byte + dir := t.TempDir() + largeFile := filepath.Join(dir, "large-message.txt") + largeContent := strings.Repeat("A", int(maxMessageSize)+1) // Exceed limit by 1 byte + err = os.WriteFile(largeFile, []byte(largeContent), 0644) + require.NoError(t, err, "Failed to create large test file") + + out, err := RunCommand(cliBinaryPath, "publish", + "--topic="+testTopic, + "--file="+largeFile, + "--service-url="+serviceURL) + require.Error(t, err, "Publish should fail when message size exceeds limit. Output: %s", out) + require.Contains(t, strings.ToLower(out), "message size", "Error should mention message size. Got: %s", out) + + cancel() + subCmd.Wait() +} + +// TestRateLimiterWithGRPC validates usage tracking with gRPC protocol +func TestRateLimiterWithGRPC(t *testing.T) { + require.NotEmpty(t, cliBinaryPath, "CLI binary path must be set by TestMain") + + serviceURL := GetDefaultProxy() + testTopic := fmt.Sprintf("ratelimit-grpc-%d", time.Now().Unix()) + + beforeCount := getInitialPublishCount(t) + + // Start subscriber + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + subCmd := exec.CommandContext(ctx, cliBinaryPath, "subscribe", "--topic="+testTopic, "--grpc", "--service-url="+serviceURL) + subCmd.Env = os.Environ() + err := subCmd.Start() + require.NoError(t, err, "Failed to start subscriber") + time.Sleep(2 * time.Second) + + // Publish via gRPC + out, err := RunCommand(cliBinaryPath, "publish", + "--topic="+testTopic, + "--message=RateLimitGRPCTest", + "--grpc", + "--service-url="+serviceURL) + require.NoError(t, err, "gRPC publish failed: %s", out) + + cancel() + subCmd.Wait() + time.Sleep(1 * time.Second) + + // Get usage stats after publishing + usageAfter, err := RunCommand(cliBinaryPath, "usage") + require.NoError(t, err, "Failed to get usage stats after publishing") + + validatorAfter := NewValidator(usageAfter) + usageInfoAfter, err := validatorAfter.ValidateUsage() + require.NoError(t, err, "Failed to parse usage stats after publishing") + + afterCount := parsePublishCount(t, usageInfoAfter.PublishCount) + + // Verify publish count increased exactly by 1 (tests not run in parallel) + require.Equal(t, beforeCount+1, afterCount, + "Publish count should increase by exactly 1 (before: %d, after: %d)", + beforeCount, afterCount) +} + +// TestRateLimiterWithFile validates usage tracking when publishing from file +func TestRateLimiterWithFile(t *testing.T) { + require.NotEmpty(t, cliBinaryPath, "CLI binary path must be set by TestMain") + + serviceURL := GetDefaultProxy() + testTopic := fmt.Sprintf("ratelimit-file-%d", time.Now().Unix()) + + dir := t.TempDir() + testFile := filepath.Join(dir, "test-publish.txt") + testContent := "Test file content for rate limit tracking" + err := os.WriteFile(testFile, []byte(testContent), 0644) + require.NoError(t, err, "Failed to create test file") + + beforeCount := getInitialPublishCount(t) + + // Start subscriber + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + subCmd := exec.CommandContext(ctx, cliBinaryPath, "subscribe", "--topic="+testTopic, "--service-url="+serviceURL) + subCmd.Env = os.Environ() + err = subCmd.Start() + require.NoError(t, err, "Failed to start subscriber") + time.Sleep(2 * time.Second) + + // Publish from file + out, err := RunCommand(cliBinaryPath, "publish", + "--topic="+testTopic, + "--file="+testFile, + "--service-url="+serviceURL) + require.NoError(t, err, "File publish failed: %s", out) + + cancel() + subCmd.Wait() + time.Sleep(1 * time.Second) + + // Get usage stats after publishing + usageAfter, err := RunCommand(cliBinaryPath, "usage") + require.NoError(t, err, "Failed to get usage stats after publishing") + + validatorAfter := NewValidator(usageAfter) + usageInfoAfter, err := validatorAfter.ValidateUsage() + require.NoError(t, err, "Failed to parse usage stats after publishing") + + afterCount := parsePublishCount(t, usageInfoAfter.PublishCount) + + // Verify publish count increased exactly by 1 (tests not run in parallel) + require.Equal(t, beforeCount+1, afterCount, + "Publish count should increase by exactly 1 (before: %d, after: %d)", + beforeCount, afterCount) +} diff --git a/e2e/ratelimit_test.go b/e2e/ratelimit_test.go index 98d66c3..be8cdd3 100644 --- a/e2e/ratelimit_test.go +++ b/e2e/ratelimit_test.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "os/exec" + "strconv" "testing" "time" @@ -19,9 +20,8 @@ func TestDailyQuotaTracking(t *testing.T) { require.NoError(t, err, "Failed to get usage stats") validator := NewValidator(usageBefore) - usageInfo, err := validator.ValidateUsage() + usageInfoBefore, err := validator.ValidateUsage() require.NoError(t, err, "Failed to parse usage stats") - t.Logf("Usage before test: %s publishes", usageInfo.PublishCount) serviceURL := GetDefaultProxy() @@ -46,9 +46,25 @@ func TestDailyQuotaTracking(t *testing.T) { usageAfter, err := RunCommand(cliBinaryPath, "usage") require.NoError(t, err, "Failed to get usage stats after publish") - t.Logf("Usage stats before: %s", usageBefore) - t.Logf("Usage stats after: %s", usageAfter) + validatorAfter := NewValidator(usageAfter) + usageInfoAfter, err := validatorAfter.ValidateUsage() + require.NoError(t, err, "Failed to parse usage stats after publish") + // Verify usage increased require.Contains(t, usageAfter, "Data Used:", "Usage stats should show data usage") - t.Log("✅ Daily quota tracking is functional") + + // Parse publish counts to verify they increased exactly by 1 (tests not run in parallel) + beforeCount := parsePublishCount(t, usageInfoBefore.PublishCount) + afterCount := parsePublishCount(t, usageInfoAfter.PublishCount) + require.Equal(t, beforeCount+1, afterCount, + "Publish count should increase by exactly 1 (before: %d, after: %d)", + beforeCount, afterCount) +} + +// parsePublishCount parses the publish count string to an integer +func parsePublishCount(t *testing.T, countStr string) int { + t.Helper() + count, err := strconv.Atoi(countStr) + require.NoError(t, err, "Failed to parse publish count '%s'", countStr) + return count } diff --git a/e2e/subscribe_test.go b/e2e/subscribe_test.go index d18e7a2..1ba62d4 100644 --- a/e2e/subscribe_test.go +++ b/e2e/subscribe_test.go @@ -18,7 +18,6 @@ func TestSubscribeCommand(t *testing.T) { serviceURL := GetDefaultProxy() // Add delay to allow P2P nodes to be ready - t.Log("Waiting for P2P nodes to be ready...") time.Sleep(3 * time.Second) testTopic := fmt.Sprintf("test-sub-%d", time.Now().Unix()) diff --git a/e2e/suite_test.go b/e2e/suite_test.go index f9a092c..093e116 100644 --- a/e2e/suite_test.go +++ b/e2e/suite_test.go @@ -12,11 +12,6 @@ var ( ) func TestMain(m *testing.M) { - if os.Getenv("MUMP2P_E2E_SKIP") == "1" { - fmt.Fprintln(os.Stderr, "[e2e] skipping CLI smoke tests (MUMP2P_E2E_SKIP=1)") - os.Exit(0) - } - var err error cliBinaryPath, cliCleanup, err = PrepareCLI() if err != nil { diff --git a/e2e/validators.go b/e2e/validators.go index 5376543..00983dc 100644 --- a/e2e/validators.go +++ b/e2e/validators.go @@ -158,13 +158,30 @@ func (v *OutputValidator) ValidateUsage() (*UsageInfo, error) { return nil, err } - // Extract publish count (number format) - publishPattern := `Publish \(hour\):\s+(\d+)` - publishCount, err := v.ExtractMatch(publishPattern) - if err != nil { - return nil, fmt.Errorf("could not extract publish count: %w", err) + // Extract publish count and limit (format: "Publish (hour): 5 / 100") + publishPattern := `Publish \(hour\):\s+(\d+)\s+/\s+(\d+)` + matches := regexp.MustCompile(publishPattern).FindStringSubmatch(v.output) + if len(matches) < 3 { + return nil, fmt.Errorf("could not extract publish count and limit") + } + info.PublishCount = matches[1] + info.PublishLimitPerHour = matches[2] + + // Extract per-second count and limit (format: "Publish (second): 1 / 2") + secondPattern := `Publish \(second\):\s+(\d+)\s+/\s+(\d+)` + secondMatches := regexp.MustCompile(secondPattern).FindStringSubmatch(v.output) + if len(secondMatches) >= 3 { + info.SecondPublishCount = secondMatches[1] + info.PublishLimitPerSec = secondMatches[2] + } + + // Extract data used and daily quota (format: "Data Used: 0.0000 MB / 100.0000 MB") + dataPattern := `Data Used:\s+([\d.]+)\s+MB\s+/\s+([\d.]+)\s+MB` + dataMatches := regexp.MustCompile(dataPattern).FindStringSubmatch(v.output) + if len(dataMatches) >= 3 { + info.BytesPublishedMB = dataMatches[1] + info.DailyQuotaMB = dataMatches[2] } - info.PublishCount = publishCount return info, nil } @@ -187,5 +204,10 @@ type HealthInfo struct { // UsageInfo holds parsed usage statistics type UsageInfo struct { - PublishCount string + PublishCount string + PublishLimitPerHour string + SecondPublishCount string + PublishLimitPerSec string + BytesPublishedMB string + DailyQuotaMB string }