diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 05100572af..f8d91d9f5a 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -90,23 +90,49 @@ jobs: go mod download - name: Start Zipkin - run: docker run -d -p 2005:9411 openzipkin/zipkin:latest + run: | + docker run -d -p 2005:9411 openzipkin/zipkin:latest || echo "⚠️ Warning: Failed to start Zipkin container (may already be running)" # Run tests with automatic retry on failures - name: Test with Retry Logic id: test uses: nick-fields/retry@v3 + continue-on-error: false with: timeout_minutes: 5 # Maximum time for the tests to run max_attempts: 2 # Retry up to 2 times if tests fail command: | + set -e export APP_ENV=test # Run tests for the examples directory with coverage go test ./examples/... -v -short -covermode=atomic -coverprofile packageWithpbgo.cov -coverpkg=./examples/... - # Filter out auto-generated files by protobuf and gofr framework from coverage report - grep -vE '(/client/|grpc-.+-client/main\.go|_client\.go|_gofr\.go|_grpc\.pb\.go|\.pb\.go|\.proto|health_.*\.go)' packageWithpbgo.cov > profile.cov - # Display coverage statistics - go tool cover -func profile.cov + exit_code=$? + if [ $exit_code -eq 2 ]; then + echo "::error::Panic detected in examples tests" + exit 2 + elif [ $exit_code -ne 0 ]; then + echo "::error::Examples tests failed with exit code $exit_code" + exit $exit_code + fi + + # Filter out auto-generated files by protobuf and gofr framework from coverage report + # Initialize profile with mode line first + echo "mode: atomic" > profile.cov + + # Use || true to prevent grep from failing if no matches found + if [ -f packageWithpbgo.cov ] && [ -s packageWithpbgo.cov ]; then + # Filter and append to profile (skip mode line) + grep -vE '(/client/|grpc-.+-client/main\.go|_client\.go|_gofr\.go|_grpc\.pb\.go|\.pb\.go|\.proto|health_.*\.go)' packageWithpbgo.cov | grep -v "^mode:" >> profile.cov || true + else + echo "⚠️ Warning: packageWithpbgo.cov not found or empty" + fi + + # Display coverage statistics (only if profile has content beyond mode line) + if [ -s profile.cov ] && [ $(wc -l < profile.cov) -gt 1 ]; then + go tool cover -func profile.cov || echo "⚠️ Warning: Failed to generate coverage report" + else + echo "⚠️ Warning: No coverage data found in profile.cov" + fi # Upload coverage report for the 1.24 Go version only - name: Upload Test Coverage @@ -148,8 +174,10 @@ jobs: with: timeout_minutes: 5 max_attempts: 2 + continue-on-error: false retry_on: error command: | + set -e export APP_ENV=test # Run tests for root gofr package @@ -178,10 +206,22 @@ jobs: # Combine coverage profiles echo "mode: atomic" > profile.cov - grep -h -v "mode:" gofr_only.cov submodules.cov | grep -v '/mock_' >> profile.cov + # Use || true to prevent grep from failing if no matches found + # Check if files exist before grepping + if [ -f gofr_only.cov ] && [ -f submodules.cov ]; then + grep -h -v "mode:" gofr_only.cov submodules.cov 2>/dev/null | grep -v '/mock_' >> profile.cov || true + elif [ -f gofr_only.cov ]; then + grep -v "mode:" gofr_only.cov 2>/dev/null | grep -v '/mock_' >> profile.cov || true + elif [ -f submodules.cov ]; then + grep -v "mode:" submodules.cov 2>/dev/null | grep -v '/mock_' >> profile.cov || true + fi - # Show coverage summary - go tool cover -func profile.cov + # Show coverage summary (only if profile has content beyond mode line) + if [ -s profile.cov ] && [ $(wc -l < profile.cov) -gt 1 ]; then + go tool cover -func profile.cov || echo "⚠️ Warning: Failed to generate coverage report" + else + echo "⚠️ Warning: No coverage data found" + fi # Upload coverage report for the 1.24 Go version only - name: Upload Test Coverage @@ -212,16 +252,31 @@ jobs: working-directory: artifacts run: | echo "mode: atomic" > merged_profile.cov - grep -h -v "mode:" ./Example-Test-Report/profile.cov ./PKG-Coverage-Report/profile.cov >> merged_profile.cov + # Check if files exist before grepping + if [ -f ./Example-Test-Report/profile.cov ] && [ -f ./PKG-Coverage-Report/profile.cov ]; then + grep -h -v "mode:" ./Example-Test-Report/profile.cov ./PKG-Coverage-Report/profile.cov >> merged_profile.cov || true + elif [ -f ./Example-Test-Report/profile.cov ]; then + grep -v "mode:" ./Example-Test-Report/profile.cov >> merged_profile.cov || true + elif [ -f ./PKG-Coverage-Report/profile.cov ]; then + grep -v "mode:" ./PKG-Coverage-Report/profile.cov >> merged_profile.cov || true + else + echo "⚠️ Warning: No coverage files found to merge" + fi # Calculate and output the total code coverage percentage - name: Parse code-coverage value working-directory: artifacts run: | - codeCoverage=$(go tool cover -func=merged_profile.cov | grep total | awk '{print $3}') - codeCoverage=${codeCoverage%?} - echo "CODE_COVERAGE=$codeCoverage" >> $GITHUB_ENV - echo "✅ Total Code Coverage: $codeCoverage%" + if [ -s merged_profile.cov ] && [ $(wc -l < merged_profile.cov) -gt 1 ]; then + codeCoverage=$(go tool cover -func=merged_profile.cov | grep total | awk '{print $3}' || echo "0.0%") + codeCoverage=${codeCoverage%?} + echo "CODE_COVERAGE=$codeCoverage" >> $GITHUB_ENV + echo "✅ Total Code Coverage: $codeCoverage%" + else + echo "⚠️ Warning: No coverage data to parse" + echo "CODE_COVERAGE=0.0" >> $GITHUB_ENV + echo "✅ Total Code Coverage: 0.0%" + fi # - name: Check if code-coverage is greater than threshold # run: | @@ -255,7 +310,11 @@ jobs: id: detect_submodules run: | # Find all directories containing a go.mod file within 'pkg' - SUBMODULES=$(find pkg -name "go.mod" -exec dirname {} \; | jq -R -s -c 'split("\n") | map(select(length > 0))') + SUBMODULES=$(find pkg -name "go.mod" -exec dirname {} \; | jq -R -s -c 'split("\n") | map(select(length > 0))' || echo '[]') + if [ "$SUBMODULES" = "[]" ] || [ -z "$SUBMODULES" ]; then + echo "⚠️ Warning: No submodules detected" + SUBMODULES='[]' + fi echo "submodules=$SUBMODULES" >> $GITHUB_OUTPUT # Test all submodules in parallel with retry logic @@ -273,27 +332,64 @@ jobs: # Get the list of submodules SUBMODULES='${{ steps.detect_submodules.outputs.submodules }}' + # Check if we have any submodules to test + if [ "$SUBMODULES" = "[]" ] || [ -z "$SUBMODULES" ]; then + echo "ℹ️ No submodules to test" + exit 0 + fi + # Process each submodule in parallel with a maximum of 4 parallel jobs - echo $SUBMODULES | jq -c '.[]' | xargs -I{} -P 4 bash -c ' + # Use a temporary file to track failures (accessible from subshells) + FAILED_MODULES_FILE=$(mktemp) + echo $SUBMODULES | jq -c '.[]' | xargs -I{} -P 4 bash -c " module={} - echo "Testing module: $module" - cd $module + echo \"Testing module: \$module\" + + if [ ! -d \"\$module\" ]; then + echo \"⚠️ Warning: Module directory \$module does not exist, skipping\" + exit 0 + fi + + if ! cd \"\$module\" 2>/dev/null; then + echo \"⚠️ Error: Failed to cd to \$module\" + echo \"\$module\" >> \"$FAILED_MODULES_FILE\" + exit 0 + fi # Extract module name (replace / with _) - module_name=$(echo $module | tr "/" "_") + module_name=\$(echo \"\$module\" | tr \"/\" \"_\") - # Download dependencies for the submodule - go mod download - go mod tidy + # Download dependencies for the submodule (non-fatal) + go mod download || echo \"⚠️ Warning: Failed to download dependencies for \$module\" + go mod tidy || echo \"⚠️ Warning: Failed to tidy dependencies for \$module\" - # Run tests with a focus on failed tests first - go test ./... -v -short -coverprofile=${module_name}.cov -coverpkg=./... + # Run tests - capture exit code (don't use set -e) + if go test ./... -v -short -coverprofile=\${module_name}.cov -coverpkg=./...; then + # Copy coverage file to the coverage_reports directory if it exists + if [ -f \"\${module_name}.cov\" ]; then + cp \"\${module_name}.cov\" ../../../coverage_reports/ || echo \"⚠️ Warning: Failed to copy coverage file for \$module\" + fi + echo \"✅ Tests passed for module \$module\" + else + echo \"❌ Tests failed for module \$module\" + echo \"\$module\" >> \"$FAILED_MODULES_FILE\" + fi - # Copy coverage file to the coverage_reports directory - cp ${module_name}.cov ../../../coverage_reports/ + cd - || true + " || true - cd - - ' + # Check if any modules failed + if [ -s "$FAILED_MODULES_FILE" ]; then + echo "::error::The following submodules had test failures:" + while IFS= read -r module; do + echo "::error:: - $module" + done < "$FAILED_MODULES_FILE" + rm -f "$FAILED_MODULES_FILE" + exit 1 + else + echo "✅ All submodule tests passed" + rm -f "$FAILED_MODULES_FILE" + fi # Upload submodule coverage reports as an artifact - name: Upload Coverage Reports @@ -334,11 +430,24 @@ jobs: working-directory: artifacts run: | echo "mode: atomic" > merged_profile.cov - grep -h -v "mode:" ./Example-Test-Report/profile.cov ./PKG-Coverage-Report/profile.cov >> merged_profile.cov + # Check if files exist before grepping + if [ -f ./Example-Test-Report/profile.cov ] && [ -f ./PKG-Coverage-Report/profile.cov ]; then + grep -h -v "mode:" ./Example-Test-Report/profile.cov ./PKG-Coverage-Report/profile.cov >> merged_profile.cov || true + elif [ -f ./Example-Test-Report/profile.cov ]; then + grep -v "mode:" ./Example-Test-Report/profile.cov >> merged_profile.cov || true + elif [ -f ./PKG-Coverage-Report/profile.cov ]; then + grep -v "mode:" ./PKG-Coverage-Report/profile.cov >> merged_profile.cov || true + else + echo "⚠️ Warning: No coverage files found to merge" + fi # Generate and print total coverage percentage - echo "Total Coverage:" - go tool cover -func=merged_profile.cov | tail -n 1 + if [ -s merged_profile.cov ] && [ $(wc -l < merged_profile.cov) -gt 1 ]; then + echo "Total Coverage:" + go tool cover -func=merged_profile.cov | tail -n 1 || echo "⚠️ Warning: Failed to generate coverage report" + else + echo "⚠️ Warning: No coverage data to display" + fi shell: bash # Upload merged coverage to CodeClimate for analysis @@ -436,6 +545,10 @@ jobs: echo "has_modules=true" >> $GITHUB_OUTPUT else echo "✅ No submodule changes detected" + echo "ℹ️ This means either:" + echo " 1. No files changed in pkg/ submodules" + echo " 2. Changed files are in root pkg/gofr (not submodules)" + echo " 3. Changed files don't have go.mod in their directory" echo "modules=[]" >> $GITHUB_OUTPUT echo "has_modules=false" >> $GITHUB_OUTPUT fi diff --git a/examples/using-subscriber/main_test.go b/examples/using-subscriber/main_test.go index 7dd7de1584..f8d3050365 100644 --- a/examples/using-subscriber/main_test.go +++ b/examples/using-subscriber/main_test.go @@ -21,6 +21,9 @@ func TestMain(m *testing.M) { } func TestMainInitialization(t *testing.T) { + // Use NewServerConfigs to get a free metrics port and avoid port conflicts + _ = testutil.NewServerConfigs(t) + log := testutil.StdoutOutputForFunc(func() { go main() diff --git a/pkg/gofr/datasource/pubsub/kafka/conn.go b/pkg/gofr/datasource/pubsub/kafka/conn.go index 0708a720fa..cac30c7087 100644 --- a/pkg/gofr/datasource/pubsub/kafka/conn.go +++ b/pkg/gofr/datasource/pubsub/kafka/conn.go @@ -60,14 +60,23 @@ func (k *kafkaClient) getNewReader(topic string) Reader { } func (k *kafkaClient) DeleteTopic(_ context.Context, name string) error { + if k.conn == nil { + return errClientNotConnected + } return k.conn.DeleteTopics(name) } func (k *kafkaClient) Controller() (broker kafka.Broker, err error) { + if k.conn == nil { + return kafka.Broker{}, errClientNotConnected + } return k.conn.Controller() } func (k *kafkaClient) CreateTopic(_ context.Context, name string) error { + if k.conn == nil { + return errClientNotConnected + } topics := kafka.TopicConfig{Topic: name, NumPartitions: 1, ReplicationFactor: 1} return k.conn.CreateTopics(topics)