Skip to content

tests: make cleanup idempotent, remove truncate path, add test metric…#124

Open
coolwater wants to merge 4 commits into
developfrom
fix/tests-idempotent-no-truncate-metrics
Open

tests: make cleanup idempotent, remove truncate path, add test metric…#124
coolwater wants to merge 4 commits into
developfrom
fix/tests-idempotent-no-truncate-metrics

Conversation

@coolwater
Copy link
Copy Markdown

Replaced table truncation cleanup with per-row delete-by-key in test helpers (real DB path).
Removed dual cleanup patterns (DeleteAllEntities + defer DeleteAllEntities) and switched to test-scoped t.Cleanup deletes.
Made tests idempotent/parallel-safe by deleting only objects created by each test and using unique IDs where needed.
Kept real DB behavior (no enabling of USE_MOCK_DB in test runner).
Fixed empty/not-found behavior expected by tests in queries services.
TOTAL: 3230,  PASSED: 3143, FAILED: 0 , SKIPPED: 87
Time: 1m 19s
Cassandra: 5.0.6
Hardware: M3 MacBook Pro

Copilot AI review requested due to automatic review settings May 14, 2026 04:58
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors test cleanup to avoid Cassandra TRUNCATE latency by deleting per-row keys, switches many tests to t.Cleanup(...) for more idempotent teardown, adds a go test -json runner that emits a per-package metrics summary, and tweaks a couple of query services to treat “not found” as an empty/idempotent case.

Changes:

  • Replace TRUNCATE-based test cleanup helpers with per-row delete-by-key logic and move several tests to t.Cleanup(...).
  • Add contrib/scripts/run_tests_with_summary.sh and wire make test to use it for JSON logs + metrics summary output.
  • Adjust query services to treat “not found” as a valid empty/idempotent scenario (percent filter and IP filter delete).

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 16 comments.

Show a summary per file
File Description
Makefile Routes make test through the new test runner script.
contrib/scripts/run_tests_with_summary.sh Runs go test -json, writes logs, and generates a metrics summary via an embedded Python parser.
adminapi/telemetry/telemetry_v2_rule_service_test.go Replaces package-wide cleanup patterns with t.Cleanup and per-entity deletes for test data.
adminapi/telemetry/telemetry_profile_handler_test.go Adds a test watchdog goroutine and changes TRUNCATE logic to per-key deletes.
adminapi/rfc/feature/feature_test_helpers_test.go Updates cleanup helper to delete rows by key rather than TRUNCATE.
adminapi/rfc/feature/feature_handler_test.go Adds test watchdog and replaces DB-wide cleanup with per-entity t.Cleanup deletes.
adminapi/queries/queries_test.go Adds test watchdog and changes TRUNCATE cleanup to per-key deletes.
adminapi/queries/percentage_bean_service_test.go Updates tests to delete only created rows and uses unique IDs in some cases.
adminapi/queries/percent_filter_service.go Treats “not found” from env/model rule lookup as an empty ruleset.
adminapi/queries/ips_filter_service.go Makes delete idempotent by treating “not found” as 204 No Content.
adminapi/dcm/logupload_settings_handler_test.go Removes global cleanup calls in favor of targeted t.Cleanup deletes.
adminapi/dcm/dcmformula_test.go Adds watchdog + per-key cleanup; updates formula IDs for uniqueness; adjusts assertions in sort-by-priority test.
Comments suppressed due to low confidence (4)

adminapi/telemetry/telemetry_v2_rule_service_test.go:342

  • This test also registers both t.Cleanup(DeleteTelemetryEntities) and a per-row delete cleanup for the same table. Consider consolidating to one cleanup strategy to avoid redundant DB work and potential noisy delete errors.
func TestFindByContext_ApplicationTypeFilter(t *testing.T) {
	DeleteTelemetryEntities()
	t.Cleanup(DeleteTelemetryEntities)
	rule1 := createTestTelemetryTwoRule("Rule1", "stb", []string{})
	rule2 := createTestTelemetryTwoRule("Rule2", "rdkcloud", []string{})

	logupload.SetOneTelemetryTwoRule(rule1.ID, rule1)
	logupload.SetOneTelemetryTwoRule(rule2.ID, rule2)

	t.Cleanup(func() {
		DeleteOneFromDao(ds.TABLE_TELEMETRY_TWO_RULES, rule1.ID)
		DeleteOneFromDao(ds.TABLE_TELEMETRY_TWO_RULES, rule2.ID)
	})

adminapi/queries/queries_test.go:141

  • truncateTable always returns nil even when individual deletes fail (delErr is only printed). Callers that check the returned error (e.g., DeleteAllEntities) will never detect cleanup failures. Consider accumulating the first/last delete error and returning it (or returning a combined error) instead of always returning nil.
		if delErr := dao.DeleteOne(tableName, keyStr); delErr != nil {
			fmt.Printf("failed to delete %s from %s: %v\n", keyStr, tableName, delErr)
		}
	}
	return nil

adminapi/dcm/dcmformula_test.go:960

  • truncateTable always returns nil even when individual deletes fail (delErr is only printed). Any callers checking the returned error won’t detect cleanup failures. Consider returning a non-nil error when any delete fails (and/or when GetKeys fails for reasons other than "empty/not found").
		if delErr := dao.DeleteOne(tableName, keyStr); delErr != nil {
			fmt.Printf("failed to delete %s from %s: %v\n", keyStr, tableName, delErr)
		}
	}
	return nil
}

adminapi/rfc/feature/feature_test_helpers_test.go:125

  • truncateTable always returns nil even when row deletes fail (delErr is only printed). This makes DeleteAllEntities unable to detect/report cleanup failures. Consider returning an error if any delete fails instead of always returning nil.
		if delErr := dao.DeleteOne(tableName, keyStr); delErr != nil {
			fmt.Printf("failed to delete %s from %s: %v\n", keyStr, tableName, delErr)
		}
	}
	return nil
}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +68 to +71
if not test and action in ("pass", "fail"):
stats[pkg]["pkg_status"] = action
if "Elapsed" in evt:
stats[pkg]["elapsed"] = float(evt["Elapsed"])
Comment on lines +24 to +25
python3 - "${JSON_LOG}" <<'PY' | tee "${SUMMARY_LOG}"
import json
Comment on lines +99 to +100
stopWatchdog := startTestWatchdog("adminapi/telemetry")
defer stopWatchdog()
Comment on lines +58 to +61
func TestMain(m *testing.M) {
stopWatchdog := startTestWatchdog("adminapi/rfc/feature")
defer stopWatchdog()

Comment on lines 143 to 147
func TestMain(m *testing.M) {
fmt.Printf("in TestMain\n")
stopWatchdog := startTestWatchdog("adminapi/queries")
defer stopWatchdog()

Comment on lines +445 to +450
dao := db.GetCachedSimpleDao()
keys, err := dao.GetKeys(tableName)
if err != nil {
// table may be empty or not yet exist; not an error
return nil
}
Comment on lines +121 to +126
dao := db.GetCachedSimpleDao()
keys, err := dao.GetKeys(tableName)
if err != nil {
// table may be empty or not yet exist; not an error
return nil
}
Comment thread adminapi/dcm/dcmformula_test.go Outdated
Comment on lines +939 to +944
dao := db.GetCachedSimpleDao()
keys, err := dao.GetKeys(tableName)
if err != nil {
// table may be empty or not yet exist; not an error
return nil
}
Comment on lines +104 to +109
dao := db.GetCachedSimpleDao()
keys, err := dao.GetKeys(tableName)
if err != nil {
// table may be empty or not yet exist; not an error
return nil
}
Comment on lines 143 to 166
// TestGetLogUploadSettingsHandler_FilterByApplicationType tests filtering by application type
func TestGetLogUploadSettingsHandler_FilterByApplicationType(t *testing.T) {
SkipIfMockDatabase(t) // Integration test
DeleteAllEntities()
defer DeleteAllEntities()

// Create formulas for different application types
formulaStb := createFormula("TEST_MODEL_STB", 1)
saveFormula(formulaStb, t)

settingsStb := &logupload.LogUploadSettings{
ID: formulaStb.ID,
Name: "STB Settings",
UploadRepositoryID: "test-repo",
ApplicationType: "stb",
Schedule: logupload.Schedule{
Type: "CronExpression",
Expression: "0 0 * * *",
TimeZone: "UTC",
},
}
CreateLogUploadSettings(settingsStb, "stb")

t.Cleanup(func() {
deleteOneFromDao(ds.TABLE_DCM_RULE, formulaStb.ID)
deleteOneFromDao(ds.TABLE_LOG_UPLOAD_SETTINGS, formulaStb.ID)
})
req := httptest.NewRequest("GET", "/xconfAdminService/dcm/logUploadSettings", nil)
Copilot AI review requested due to automatic review settings May 15, 2026 21:21
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 9 comments.

Comment on lines +46 to +58
func initMockGroupService() {
mockGroupServiceOnce.Do(func() {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
groups := &proto_generated.XdasHashes{Fields: map[string]string{}}
data, _ := proto.Marshal(groups)
w.Header().Set("Content-Type", "application/x-protobuf")
w.WriteHeader(http.StatusOK)
_, _ = w.Write(data)
}))

mockGroupServiceURL = server.URL
mockGroupServiceHTTP = server.Client()
})
mockGroupServiceOnce.Do(func() {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
groups := &proto_generated.XdasHashes{Fields: map[string]string{}}
data, _ := proto.Marshal(groups)
Comment on lines 143 to 147
func TestMain(m *testing.M) {
fmt.Printf("in TestMain\n")
stopWatchdog := startTestWatchdog("adminapi/queries")
defer stopWatchdog()

Comment on lines 120 to 141
func truncateTable(tableName string) error {
dbClient := db.GetDatabaseClient()
cassandraClient, ok := dbClient.(*db.CassandraClient)
if ok {
return cassandraClient.DeleteAllXconfData(tableName)
dao := db.GetCachedSimpleDao()
keys, err := dao.GetKeys(tableName)
if err != nil {
// table may be empty or not yet exist; not an error
return nil
}
for _, key := range keys {
var keyStr string
switch k := key.(type) {
case string:
keyStr = k
case []byte:
keyStr = string(k)
default:
keyStr = fmt.Sprint(k)
}
if delErr := dao.DeleteOne(tableName, keyStr); delErr != nil {
fmt.Printf("failed to delete %s from %s: %v\n", keyStr, tableName, delErr)
}
}
return nil
Comment on lines 113 to 134
func truncateTable(tableName string) error {
dbClient := db.GetDatabaseClient()
cassandraClient, ok := dbClient.(*db.CassandraClient)
if ok {
return cassandraClient.DeleteAllXconfData(tableName)
dao := db.GetCachedSimpleDao()
keys, err := dao.GetKeys(tableName)
if err != nil {
// table may be empty or not yet exist; not an error
return nil
}
for _, key := range keys {
var keyStr string
switch k := key.(type) {
case string:
keyStr = k
case []byte:
keyStr = string(k)
default:
keyStr = fmt.Sprint(k)
}
if delErr := dao.DeleteOne(tableName, keyStr); delErr != nil {
fmt.Printf("failed to delete %s from %s: %v\n", keyStr, tableName, delErr)
}
}
return nil
Comment thread Makefile

test:
ulimit -n 10000 ; go test ./... -p 1 -parallel 1 -cover -count=1 -timeout=45m
bash contrib/scripts/run_tests_with_summary.sh
Comment on lines +19 to +26
set +e
go test ./... -json -p 1 -parallel 1 -cover -count=1 -timeout=45m | tee "${JSON_LOG}"
TEST_EXIT=${PIPESTATUS[0]}
set -e

python3 - "${JSON_LOG}" <<'PY' | tee "${SUMMARY_LOG}"
import json
import sys
Comment on lines 80 to 88
firmwareRules, err := corefw.GetEnvModelFirmwareRules(applicationType)
if err != nil {
if strings.Contains(strings.ToLower(err.Error()), "not found") {
// Empty rules set is valid; return default percent filter.
return percentFilterValue, nil
}
log.Error(fmt.Sprintf("GetPercentFilter: %v", err))
return nil, err
}
Comment on lines 66 to 74
func DeleteIpsFilter(name string, applicationType string) *xwhttp.ResponseEntity {
ipFilter, err := coreef.IpFilterByName(name, applicationType)
if err != nil {
if strings.Contains(strings.ToLower(err.Error()), "not found") {
// Deleting a missing filter is treated as an idempotent no-op.
return xwhttp.NewResponseEntity(http.StatusNoContent, nil, nil)
}
return xwhttp.NewResponseEntity(http.StatusInternalServerError, err, nil)
}
…t filter services; update tests to expect error responses
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants