Skip to content

Commit 5b98ee1

Browse files
narrowizardclaude
andauthored
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 <noreply@anthropic.com> * 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 <noreply@anthropic.com>
1 parent 44a7227 commit 5b98ee1

8 files changed

Lines changed: 121 additions & 9 deletions

backend/core/models/domainlayer/qa/qa_test_case_execution.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type QaTestCaseExecution struct {
3333
FinishTime time.Time `gorm:"comment:Test finish time"`
3434
CreatorId string `gorm:"type:varchar(255);comment:Executor ID"`
3535
Status string `gorm:"type:varchar(255);comment:Test execution status | PENDING | IN_PROGRESS | SUCCESS | FAILED"` // enum, using string
36+
IsInvalid bool
3637
}
3738

3839
func (QaTestCaseExecution) TableName() string {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package migrationscripts
19+
20+
import (
21+
"github.com/apache/incubator-devlake/core/context"
22+
"github.com/apache/incubator-devlake/core/errors"
23+
"github.com/apache/incubator-devlake/core/plugin"
24+
)
25+
26+
var _ plugin.MigrationScript = (*addIsInvalidToQaTestCaseExecution)(nil)
27+
28+
type qaTestCaseExecution20260313 struct {
29+
IsInvalid bool
30+
}
31+
32+
func (qaTestCaseExecution20260313) TableName() string {
33+
return "qa_test_case_executions"
34+
}
35+
36+
type addIsInvalidToQaTestCaseExecution struct{}
37+
38+
func (*addIsInvalidToQaTestCaseExecution) Up(basicRes context.BasicRes) errors.Error {
39+
db := basicRes.GetDal()
40+
if err := db.AutoMigrate(&qaTestCaseExecution20260313{}); err != nil {
41+
return err
42+
}
43+
return nil
44+
}
45+
46+
func (*addIsInvalidToQaTestCaseExecution) Version() uint64 {
47+
return 20260313100000
48+
}
49+
50+
func (*addIsInvalidToQaTestCaseExecution) Name() string {
51+
return "add is_invalid to qa_test_case_executions"
52+
}

backend/core/models/migrationscripts/register.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ func All() []plugin.MigrationScript {
136136
new(addCqIssueImpacts),
137137
new(addDueDateToIssues),
138138
new(createQaTables),
139+
new(addIsInvalidToQaTestCaseExecution),
139140
new(increaseCqIssueComponentLength),
140141
new(extendFieldSizeForCq),
141142
new(addIssueFixVerion),

backend/plugins/customize/e2e/import_test_case_execution_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ func TestImportQaTestCaseExecutionsDataFlow(t *testing.T) {
7171
"start_time",
7272
"finish_time",
7373
"creator_id",
74+
"is_invalid",
7475
"status",
7576
})
7677
dataflowTester.VerifyTableWithRawData(
@@ -104,6 +105,7 @@ func TestImportQaTestCaseExecutionsDataFlow(t *testing.T) {
104105
"start_time",
105106
"finish_time",
106107
"creator_id",
108+
"is_invalid",
107109
"status",
108110
})
109111

@@ -117,3 +119,51 @@ func TestImportQaTestCaseExecutionsDataFlow(t *testing.T) {
117119
},
118120
)
119121
}
122+
123+
// TestImportQaTestCaseExecutions_NoIsInvalidColumn tests backward compatibility:
124+
// Verifies that importing CSV files without the is_invalid column works correctly,
125+
// and the is_invalid field defaults to false.
126+
func TestImportQaTestCaseExecutions_NoIsInvalidColumn(t *testing.T) {
127+
var plugin impl.Customize
128+
dataflowTester := e2ehelper.NewDataFlowTester(t, "customize", plugin)
129+
130+
// Flush the relevant table
131+
dataflowTester.FlushTabler(&qa.QaTestCaseExecution{})
132+
dataflowTester.FlushTabler(&crossdomain.Account{})
133+
134+
// Create a new service instance
135+
svc := service.NewService(dataflowTester.Dal)
136+
137+
// Use the existing CSV file that does NOT contain is_invalid column
138+
// This simulates backward compatibility with old CSV files
139+
qaTestCaseExecutionsFile, err := os.Open("raw_tables/qa_test_case_executions_input.csv")
140+
if err != nil {
141+
t.Fatal(err)
142+
}
143+
defer qaTestCaseExecutionsFile.Close()
144+
145+
// Define a dummy qaProjectId
146+
qaProjectId := "test-backward-compat-project"
147+
148+
// Import data from the CSV file (which has no is_invalid column)
149+
err = svc.ImportQaTestCaseExecutions(qaProjectId, qaTestCaseExecutionsFile, false)
150+
if err != nil {
151+
t.Fatalf("ImportQaTestCaseExecutions failed: %v", err)
152+
}
153+
154+
// Verify the imported data has is_invalid defaulted to false
155+
dataflowTester.VerifyTableWithRawData(
156+
&qa.QaTestCaseExecution{},
157+
"snapshot_tables/qa_test_case_executions_output_no_is_invalid_column.csv",
158+
[]string{
159+
"id",
160+
"qa_project_id",
161+
"qa_test_case_id",
162+
"create_time",
163+
"start_time",
164+
"finish_time",
165+
"creator_id",
166+
"is_invalid",
167+
"status",
168+
})
169+
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
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
2-
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,,,
3-
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,,,
4-
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,,,
1+
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
2+
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,,,
3+
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,,,
4+
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,,,
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
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
2-
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,,,
3-
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,,,
4-
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,,,
5-
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,,,
1+
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
2+
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,,,
3+
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,,,
4+
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,,,
5+
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,,,
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
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
2+
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,,,
3+
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,,,
4+
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,,,

backend/plugins/customize/service/service.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,10 @@ func (s *Service) qaTestCaseExecutionHandler(qaProjectId string) func(record map
541541
}
542542
delete(record, "creator_name")
543543
record["qa_project_id"] = qaProjectId
544+
// Set default value for is_invalid if not present or empty in the CSV
545+
if isInvalid, exists := record["is_invalid"]; !exists || isInvalid == "" {
546+
record["is_invalid"] = false
547+
}
544548
return s.dal.CreateWithMap(&qa.QaTestCaseExecution{}, record)
545549
}
546550
}

0 commit comments

Comments
 (0)