This is an automated email from the ASF dual-hosted git repository.
zky pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/main by this push:
new 5b98ee1e7 feat(qa): add is_invalid field to qa_test_case_executions
(#8764)
5b98ee1e7 is described below
commit 5b98ee1e74cd9ca89035ba47bc6fe0d66c2c1ed4
Author: NaRro <[email protected]>
AuthorDate: Tue Mar 17 02:19:11 2026 +0000
feat(qa): add is_invalid field to qa_test_case_executions (#8764)
* feat(qa): add is_invalid field to qa_test_case_executions
Add is_invalid boolean field to the domain layer qa_test_case_executions
table to allow QA teams to flag test executions as invalid due to
environmental issues, flaky tests, false positives, or false negatives.
Changes:
- Add IsInvalid field to QaTestCaseExecution domain model
- Create migration script
(20260313_add_is_invalid_to_qa_test_case_executions)
- Register migration in migrationscripts/register.go
- Update customize service to set default value for is_invalid
- Update E2E test data to include new column
Resolves #8763
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix(qa): handle missing is_invalid column in CSV import
Fix PostgreSQL compatibility issue when CSV files don't contain
the is_invalid column. The field now defaults to false instead
of an empty string.
Changes:
- Update qaTestCaseExecutionHandler to check for empty string values
- Add E2E test for backward compatibility with CSV files lacking is_invalid
- Add explicit IsInvalid initialization in Testmo plugin converter
Resolves #8763
---------
Co-authored-by: Claude Opus 4.6 <[email protected]>
---
.../domainlayer/qa/qa_test_case_execution.go | 1 +
...13_add_is_invalid_to_qa_test_case_executions.go | 52 ++++++++++++++++++++++
backend/core/models/migrationscripts/register.go | 1 +
.../e2e/import_test_case_execution_test.go | 50 +++++++++++++++++++++
.../qa_test_case_executions_output.csv | 8 ++--
.../qa_test_case_executions_output_incremental.csv | 10 ++---
...case_executions_output_no_is_invalid_column.csv | 4 ++
backend/plugins/customize/service/service.go | 4 ++
8 files changed, 121 insertions(+), 9 deletions(-)
diff --git a/backend/core/models/domainlayer/qa/qa_test_case_execution.go
b/backend/core/models/domainlayer/qa/qa_test_case_execution.go
index cfab89b6d..aaf237b6f 100644
--- a/backend/core/models/domainlayer/qa/qa_test_case_execution.go
+++ b/backend/core/models/domainlayer/qa/qa_test_case_execution.go
@@ -33,6 +33,7 @@ type QaTestCaseExecution struct {
FinishTime time.Time `gorm:"comment:Test finish time"`
CreatorId string `gorm:"type:varchar(255);comment:Executor ID"`
Status string `gorm:"type:varchar(255);comment:Test execution
status | PENDING | IN_PROGRESS | SUCCESS | FAILED"` // enum, using string
+ IsInvalid bool
}
func (QaTestCaseExecution) TableName() string {
diff --git
a/backend/core/models/migrationscripts/20260313_add_is_invalid_to_qa_test_case_executions.go
b/backend/core/models/migrationscripts/20260313_add_is_invalid_to_qa_test_case_executions.go
new file mode 100644
index 000000000..ce7656e1e
--- /dev/null
+++
b/backend/core/models/migrationscripts/20260313_add_is_invalid_to_qa_test_case_executions.go
@@ -0,0 +1,52 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package migrationscripts
+
+import (
+ "github.com/apache/incubator-devlake/core/context"
+ "github.com/apache/incubator-devlake/core/errors"
+ "github.com/apache/incubator-devlake/core/plugin"
+)
+
+var _ plugin.MigrationScript = (*addIsInvalidToQaTestCaseExecution)(nil)
+
+type qaTestCaseExecution20260313 struct {
+ IsInvalid bool
+}
+
+func (qaTestCaseExecution20260313) TableName() string {
+ return "qa_test_case_executions"
+}
+
+type addIsInvalidToQaTestCaseExecution struct{}
+
+func (*addIsInvalidToQaTestCaseExecution) Up(basicRes context.BasicRes)
errors.Error {
+ db := basicRes.GetDal()
+ if err := db.AutoMigrate(&qaTestCaseExecution20260313{}); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (*addIsInvalidToQaTestCaseExecution) Version() uint64 {
+ return 20260313100000
+}
+
+func (*addIsInvalidToQaTestCaseExecution) Name() string {
+ return "add is_invalid to qa_test_case_executions"
+}
diff --git a/backend/core/models/migrationscripts/register.go
b/backend/core/models/migrationscripts/register.go
index 5b682b662..9372d8fbd 100644
--- a/backend/core/models/migrationscripts/register.go
+++ b/backend/core/models/migrationscripts/register.go
@@ -136,6 +136,7 @@ func All() []plugin.MigrationScript {
new(addCqIssueImpacts),
new(addDueDateToIssues),
new(createQaTables),
+ new(addIsInvalidToQaTestCaseExecution),
new(increaseCqIssueComponentLength),
new(extendFieldSizeForCq),
new(addIssueFixVerion),
diff --git a/backend/plugins/customize/e2e/import_test_case_execution_test.go
b/backend/plugins/customize/e2e/import_test_case_execution_test.go
index 92d5750b8..a74c69940 100644
--- a/backend/plugins/customize/e2e/import_test_case_execution_test.go
+++ b/backend/plugins/customize/e2e/import_test_case_execution_test.go
@@ -71,6 +71,7 @@ func TestImportQaTestCaseExecutionsDataFlow(t *testing.T) {
"start_time",
"finish_time",
"creator_id",
+ "is_invalid",
"status",
})
dataflowTester.VerifyTableWithRawData(
@@ -104,6 +105,7 @@ func TestImportQaTestCaseExecutionsDataFlow(t *testing.T) {
"start_time",
"finish_time",
"creator_id",
+ "is_invalid",
"status",
})
@@ -117,3 +119,51 @@ func TestImportQaTestCaseExecutionsDataFlow(t *testing.T) {
},
)
}
+
+// TestImportQaTestCaseExecutions_NoIsInvalidColumn tests backward
compatibility:
+// Verifies that importing CSV files without the is_invalid column works
correctly,
+// and the is_invalid field defaults to false.
+func TestImportQaTestCaseExecutions_NoIsInvalidColumn(t *testing.T) {
+ var plugin impl.Customize
+ dataflowTester := e2ehelper.NewDataFlowTester(t, "customize", plugin)
+
+ // Flush the relevant table
+ dataflowTester.FlushTabler(&qa.QaTestCaseExecution{})
+ dataflowTester.FlushTabler(&crossdomain.Account{})
+
+ // Create a new service instance
+ svc := service.NewService(dataflowTester.Dal)
+
+ // Use the existing CSV file that does NOT contain is_invalid column
+ // This simulates backward compatibility with old CSV files
+ qaTestCaseExecutionsFile, err :=
os.Open("raw_tables/qa_test_case_executions_input.csv")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer qaTestCaseExecutionsFile.Close()
+
+ // Define a dummy qaProjectId
+ qaProjectId := "test-backward-compat-project"
+
+ // Import data from the CSV file (which has no is_invalid column)
+ err = svc.ImportQaTestCaseExecutions(qaProjectId,
qaTestCaseExecutionsFile, false)
+ if err != nil {
+ t.Fatalf("ImportQaTestCaseExecutions failed: %v", err)
+ }
+
+ // Verify the imported data has is_invalid defaulted to false
+ dataflowTester.VerifyTableWithRawData(
+ &qa.QaTestCaseExecution{},
+
"snapshot_tables/qa_test_case_executions_output_no_is_invalid_column.csv",
+ []string{
+ "id",
+ "qa_project_id",
+ "qa_test_case_id",
+ "create_time",
+ "start_time",
+ "finish_time",
+ "creator_id",
+ "is_invalid",
+ "status",
+ })
+}
diff --git
a/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output.csv
b/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output.csv
index 1d51d4a0d..cd78e9ec5 100644
---
a/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output.csv
+++
b/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output.csv
@@ -1,4 +1,4 @@
-id,qa_project_id,qa_test_case_id,create_time,start_time,finish_time,creator_id,status,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
-exec-1,test-qa-project-id,tc-1,2023-03-01T10:00:00.000+00:00,2023-03-01T10:01:00.000+00:00,2023-03-01T10:05:00.000+00:00,csv:CsvAccount:0:user-a,SUCCESS,test-qa-project-id,,,
-exec-2,test-qa-project-id,tc-2,2023-03-01T11:00:00.000+00:00,2023-03-01T11:02:00.000+00:00,2023-03-01T11:06:00.000+00:00,csv:CsvAccount:0:user-b,FAILED,test-qa-project-id,,,
-exec-3,test-qa-project-id,tc-1,2023-03-02T10:00:00.000+00:00,2023-03-02T10:01:00.000+00:00,2023-03-02T10:04:00.000+00:00,csv:CsvAccount:0:user-a,SUCCESS,test-qa-project-id,,,
+id,qa_project_id,qa_test_case_id,create_time,start_time,finish_time,creator_id,is_invalid,status,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+exec-1,test-qa-project-id,tc-1,2023-03-01T10:00:00.000+00:00,2023-03-01T10:01:00.000+00:00,2023-03-01T10:05:00.000+00:00,csv:CsvAccount:0:user-a,0,SUCCESS,test-qa-project-id,,,
+exec-2,test-qa-project-id,tc-2,2023-03-01T11:00:00.000+00:00,2023-03-01T11:02:00.000+00:00,2023-03-01T11:06:00.000+00:00,csv:CsvAccount:0:user-b,0,FAILED,test-qa-project-id,,,
+exec-3,test-qa-project-id,tc-1,2023-03-02T10:00:00.000+00:00,2023-03-02T10:01:00.000+00:00,2023-03-02T10:04:00.000+00:00,csv:CsvAccount:0:user-a,0,SUCCESS,test-qa-project-id,,,
diff --git
a/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output_incremental.csv
b/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output_incremental.csv
index e214d734b..90e496a27 100644
---
a/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output_incremental.csv
+++
b/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output_incremental.csv
@@ -1,5 +1,5 @@
-id,qa_project_id,qa_test_case_id,create_time,start_time,finish_time,creator_id,status,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
-exec-1,test-qa-project-id,tc-1,2023-03-04T10:00:00.000+00:00,2023-03-04T10:01:00.000+00:00,2023-03-04T10:06:00.000+00:00,csv:CsvAccount:0:user-a,FAILED,test-qa-project-id,,,
-exec-2,test-qa-project-id,tc-2,2023-03-01T11:00:00.000+00:00,2023-03-01T11:02:00.000+00:00,2023-03-01T11:06:00.000+00:00,csv:CsvAccount:0:user-b,FAILED,test-qa-project-id,,,
-exec-3,test-qa-project-id,tc-1,2023-03-02T10:00:00.000+00:00,2023-03-02T10:01:00.000+00:00,2023-03-02T10:04:00.000+00:00,csv:CsvAccount:0:user-a,SUCCESS,test-qa-project-id,,,
-exec-4,test-qa-project-id,tc-3,2023-03-03T10:00:00.000+00:00,2023-03-03T10:01:00.000+00:00,2023-03-03T10:05:00.000+00:00,csv:CsvAccount:0:user-c,SUCCESS,test-qa-project-id,,,
+id,qa_project_id,qa_test_case_id,create_time,start_time,finish_time,creator_id,is_invalid,status,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+exec-1,test-qa-project-id,tc-1,2023-03-04T10:00:00.000+00:00,2023-03-04T10:01:00.000+00:00,2023-03-04T10:06:00.000+00:00,csv:CsvAccount:0:user-a,0,FAILED,test-qa-project-id,,,
+exec-2,test-qa-project-id,tc-2,2023-03-01T11:00:00.000+00:00,2023-03-01T11:02:00.000+00:00,2023-03-01T11:06:00.000+00:00,csv:CsvAccount:0:user-b,0,FAILED,test-qa-project-id,,,
+exec-3,test-qa-project-id,tc-1,2023-03-02T10:00:00.000+00:00,2023-03-02T10:01:00.000+00:00,2023-03-02T10:04:00.000+00:00,csv:CsvAccount:0:user-a,0,SUCCESS,test-qa-project-id,,,
+exec-4,test-qa-project-id,tc-3,2023-03-03T10:00:00.000+00:00,2023-03-03T10:01:00.000+00:00,2023-03-03T10:05:00.000+00:00,csv:CsvAccount:0:user-c,0,SUCCESS,test-qa-project-id,,,
diff --git
a/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output_no_is_invalid_column.csv
b/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output_no_is_invalid_column.csv
new file mode 100644
index 000000000..e313377a0
--- /dev/null
+++
b/backend/plugins/customize/e2e/snapshot_tables/qa_test_case_executions_output_no_is_invalid_column.csv
@@ -0,0 +1,4 @@
+id,qa_project_id,qa_test_case_id,create_time,start_time,finish_time,creator_id,is_invalid,status,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+exec-1,test-backward-compat-project,tc-1,2023-03-01T10:00:00.000+00:00,2023-03-01T10:01:00.000+00:00,2023-03-01T10:05:00.000+00:00,csv:CsvAccount:0:user-a,0,SUCCESS,test-backward-compat-project,,,
+exec-2,test-backward-compat-project,tc-2,2023-03-01T11:00:00.000+00:00,2023-03-01T11:02:00.000+00:00,2023-03-01T11:06:00.000+00:00,csv:CsvAccount:0:user-b,0,FAILED,test-backward-compat-project,,,
+exec-3,test-backward-compat-project,tc-1,2023-03-02T10:00:00.000+00:00,2023-03-02T10:01:00.000+00:00,2023-03-02T10:04:00.000+00:00,csv:CsvAccount:0:user-a,0,SUCCESS,test-backward-compat-project,,,
\ No newline at end of file
diff --git a/backend/plugins/customize/service/service.go
b/backend/plugins/customize/service/service.go
index 89e8833cf..5ef3bcbb3 100644
--- a/backend/plugins/customize/service/service.go
+++ b/backend/plugins/customize/service/service.go
@@ -541,6 +541,10 @@ func (s *Service) qaTestCaseExecutionHandler(qaProjectId
string) func(record map
}
delete(record, "creator_name")
record["qa_project_id"] = qaProjectId
+ // Set default value for is_invalid if not present or empty in
the CSV
+ if isInvalid, exists := record["is_invalid"]; !exists ||
isInvalid == "" {
+ record["is_invalid"] = false
+ }
return s.dal.CreateWithMap(&qa.QaTestCaseExecution{}, record)
}
}