From 6dab7e62a016727cd568c1cfad193f5c99f2a68f Mon Sep 17 00:00:00 2001 From: "Giau. Tran Minh" Date: Wed, 18 Dec 2024 15:42:56 +0700 Subject: [PATCH] atlasaction: move template test to testscript --- .gitignore | 3 + atlasaction/action.go | 8 +- atlasaction/action_test.go | 441 ++---------------- .../testdata/templates/migrate-apply.txtar | 191 ++++++++ .../testdata/templates/migrate-lint.txtar | 117 +++++ 5 files changed, 357 insertions(+), 403 deletions(-) create mode 100644 atlasaction/testdata/templates/migrate-apply.txtar create mode 100644 atlasaction/testdata/templates/migrate-lint.txtar diff --git a/.gitignore b/.gitignore index b1b7112d..acd83baa 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,6 @@ lib/**/* # IDE files .idea/ + +# the binary file for the atlas-action +atlas-action diff --git a/atlasaction/action.go b/atlasaction/action.go index a3217cf8..609e5513 100644 --- a/atlasaction/action.go +++ b/atlasaction/action.go @@ -1087,9 +1087,9 @@ func appliedStmts(a *atlasexec.MigrateApply) int { } var ( - //go:embed comments/*.tmpl + //go:embed comments comments embed.FS - commentsTmpl = template.Must( + CommentsTmpl = template.Must( template.New("comments"). Funcs(template.FuncMap{ "execTime": execTime, @@ -1155,14 +1155,14 @@ var ( return fmt.Sprintf(``, src, attrs), nil }, }). - ParseFS(comments, "comments/*.tmpl"), + ParseFS(comments, "comments/*"), ) ) // RenderTemplate renders the given template with the data. func RenderTemplate(name string, data any) (string, error) { var buf bytes.Buffer - if err := commentsTmpl.ExecuteTemplate(&buf, name, data); err != nil { + if err := CommentsTmpl.ExecuteTemplate(&buf, name, data); err != nil { return "", err } return buf.String(), nil diff --git a/atlasaction/action_test.go b/atlasaction/action_test.go index 157feae9..f83ed665 100644 --- a/atlasaction/action_test.go +++ b/atlasaction/action_test.go @@ -24,14 +24,11 @@ import ( "slices" "strings" "testing" - "time" "ariga.io/atlas-action/atlasaction" "ariga.io/atlas-action/atlasaction/cloud" "ariga.io/atlas-go-sdk/atlasexec" "ariga.io/atlas/sql/migrate" - "ariga.io/atlas/sql/sqlcheck" - "ariga.io/atlas/sql/sqlclient" _ "github.com/mattn/go-sqlite3" "github.com/rogpeppe/go-internal/diff" "github.com/rogpeppe/go-internal/testscript" @@ -1431,402 +1428,6 @@ func TestMigrateLint(t *testing.T) { }) } -func TestLintTemplateGeneration(t *testing.T) { - type env struct { - Driver string `json:"Driver,omitempty"` - URL *sqlclient.URL `json:"URL,omitempty"` - Dir string `json:"Dir,omitempty"` - } - for _, tt := range []struct { - name string - payload *atlasexec.SummaryReport - expected string // expected HTML output of the comment template - }{ - { - name: "no errors", - payload: &atlasexec.SummaryReport{ - URL: "https://migration-lint-report-url", - Steps: []*atlasexec.StepReport{ - { - Name: "Migration Integrity Check", - Text: "File atlas.sum is valid", - }, - { - Name: "Detect New Migration Files", - Text: "Found 1 new migration files (from 1 total)", - }, - }, - Env: env{ - Dir: "testdata/migrations", - }, - Files: []*atlasexec.FileReport{{Name: "20230925192914.sql"}}, - }, - // language=html - expected: "atlas migrate lint on testdata/migrations\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
StatusStepResult
1 new migration file detected20230925192914.sql
ERD and visual diff generatedView Visualization
No issues foundView Report
Read the full linting report on Atlas Cloud
", - }, - { - name: "file with 2 issues", - payload: &atlasexec.SummaryReport{ - URL: "https://migration-lint-report-url", - Env: env{ - Dir: ".", - }, - Steps: []*atlasexec.StepReport{ - { - Name: "Migration Integrity Check", - Text: "File atlas.sum is valid", - }, - { - Name: "Detect New Migration Files", - Text: "Found 1 new migration files (from 1 total)", - }, - { - Name: "Analyze 20230925192914.sql", - Text: "2 reports were found in analysis", - Result: &atlasexec.FileReport{ - Name: "20230925192914.sql", - Text: "CREATE UNIQUE INDEX idx_unique_fullname ON Persons (FirstName, LastName);\nALTER TABLE Persons ADD City varchar(255) NOT NULL;\n", - Reports: []sqlcheck.Report{ - { - Text: "data dependent changes detected", - Diagnostics: []sqlcheck.Diagnostic{ - { - Text: "Adding a unique index \"idx_unique_fullname\" on table \"Persons\" might fail in case columns \"FirstName\", \"LastName\" contain duplicate entries", - Code: "MF101", - }, - { - Text: "Adding a non-nullable \"varchar\" column \"City\" on table \"Persons\" without a default value implicitly sets existing rows with \"\"", - Code: "MY101", - }, - }, - }, - }, - }, - }, - }, - Files: []*atlasexec.FileReport{{ - Name: "20230925192914.sql", - Reports: []sqlcheck.Report{ - { - Diagnostics: []sqlcheck.Diagnostic{ - { - Text: "Add unique index to existing column", - Code: "MF101", - }, - { - Text: "Adding a non-nullable column to a table without a DEFAULT", - Code: "MY101", - }, - }, - }, - }, - }}, - }, - // language=html - expected: "atlas migrate lint on working directory\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
StatusStepResult
1 new migration file detected20230925192914.sql
ERD and visual diff generatedView Visualization
Analyze 20230925192914.sql
2 reports were found in analysis
Data dependent changes detected
Adding a unique index \"idx_unique_fullname\" on table \"Persons\" might fail in case columns \"FirstName\", \"LastName\" contain duplicate entries (MF101)
Adding a non-nullable \"varchar\" column \"City\" on table \"Persons\" without a default value implicitly sets existing rows with \"\" (MY101)
Read the full linting report on Atlas Cloud
", - }, - { - name: "2 files, 1 with error, 1 with issue", - payload: &atlasexec.SummaryReport{ - URL: "https://migration-lint-report-url", - Env: env{ - Dir: "testdata/migrations", - }, - Steps: []*atlasexec.StepReport{ - { - Name: "Migration Integrity Check", - Text: "File atlas.sum is valid", - }, - { - Name: "Detect New Migration Files", - Text: "Found 1 new migration files (from 1 total)", - }, - { - Name: "Analyze 20230925192914.sql", - Text: "1 reports were found in analysis", - Result: &atlasexec.FileReport{ - Name: "20230925192914.sql", - Text: "CREATE UNIQUE INDEX idx_unique_fullname ON Persons (FirstName, LastName);", - Reports: []sqlcheck.Report{ - { - Text: "data dependent changes detected", - Diagnostics: []sqlcheck.Diagnostic{ - { - Text: "Adding a unique index \"idx_unique_fullname\" on table \"Persons\" might fail in case columns \"FirstName\", \"LastName\" contain duplicate entries", - Code: "MF101", - }, - }, - }, - }, - }, - }, - { - Name: "Analyze 20240625104520_destructive.sql", - Text: "1 reports were found in analysis", - Result: &atlasexec.FileReport{ - Error: "Destructive changes detected", - Name: "20240625104520_destructive.sql", - Text: "DROP TABLE Persons;\n\n", - Reports: []sqlcheck.Report{ - { - Text: "destructive changes detected", - Diagnostics: []sqlcheck.Diagnostic{ - { - Text: "Dropping table \"Persons\"", - Code: "DS102", - }, - }, - }, - }, - }, - }, - }, - Files: []*atlasexec.FileReport{{ - Name: "20230925192914.sql", - Error: "Destructive changes detected", - }, - { - Name: "20230925192915.sql", - Reports: []sqlcheck.Report{ - { - Diagnostics: []sqlcheck.Diagnostic{ - { - Text: "Missing the CONCURRENTLY in index creation", - Code: "PG101", - }, - }, - }, - }, - }, - }, - }, - // language=html - expected: "atlas migrate lint on testdata/migrations\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
StatusStepResult
2 new migration files detected20230925192914.sql
20230925192915.sql
ERD and visual diff generatedView Visualization
Analyze 20230925192914.sql
1 reports were found in analysis
Data dependent changes detected
Adding a unique index \"idx_unique_fullname\" on table \"Persons\" might fail in case columns \"FirstName\", \"LastName\" contain duplicate entries (MF101)
Analyze 20240625104520_destructive.sql
1 reports were found in analysis
Destructive changes detected
Dropping table \"Persons\" (DS102)
Read the full linting report on Atlas Cloud
", - }, - { - name: "1 checksum error", - payload: &atlasexec.SummaryReport{ - URL: "https://migration-lint-report-url", - Env: env{ - Dir: "testdata/migrations", - }, - Steps: []*atlasexec.StepReport{ - { - Name: "Migration Integrity Check", - Text: "File atlas.sum is invalid", - Error: "checksum mismatch", - }, - }, - Files: []*atlasexec.FileReport{{ - Name: "20230925192914.sql", - Error: "checksum mismatch", - }}, - }, - // language=html - expected: "atlas migrate lint on testdata/migrations\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
StatusStepResult
1 new migration file detected20230925192914.sql
ERD and visual diff generatedView Visualization
Migration Integrity Check
File atlas.sum is invalid
checksum mismatch
Read the full linting report on Atlas Cloud
", - }, - { - name: "non linear history error", - payload: &atlasexec.SummaryReport{ - URL: "https://migration-lint-report-url", - Env: env{ - Dir: "testdata/migrations", - }, - Steps: []*atlasexec.StepReport{ - { - Name: "Migration Integrity Check", - Text: "File atlas.sum is valid", - }, - { - Name: "Detected 1 non-additive change", - Text: "Pulling the the latest git changes might fix this warning", - Result: &atlasexec.FileReport{ - Reports: []sqlcheck.Report{ - { - Diagnostics: []sqlcheck.Diagnostic{ - { - Pos: 0, - Text: "File 20240613102407.sql is missing or has been removed. Changes that have already been applied will not be reverted", - }, - }, - }, - }, - }, - }, - }, - }, - // language=html - expected: "atlas migrate lint on testdata/migrations\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
StatusStepResult
No migration files detected 
ERD and visual diff generatedView Visualization
Detected 1 non-additive change
Pulling the the latest git changes might fix this warning
File 20240613102407.sql is missing or has been removed. Changes that have already been applied will not be reverted
Read the full linting report on Atlas Cloud
", - }, - } { - t.Run(tt.name, func(t *testing.T) { - c, err := atlasaction.RenderTemplate("migrate-lint.tmpl", tt.payload) - require.NoError(t, err) - require.Equal(t, tt.expected, c) - }) - } -} - -func TestApplyTemplateGeneration(t *testing.T) { - for _, tt := range []struct { - name string - payload *atlasexec.MigrateApply - expected string // expected output of the comment template - }{ - { - name: "first apply, 2 files, 3 statements", - payload: &atlasexec.MigrateApply{ - Env: atlasexec.Env{ - Driver: "sqlite", - Dir: "testdata/migrations", - URL: &sqlclient.URL{ - URL: &url.URL{ - Scheme: "sqlite", - Host: "file", - RawQuery: "_fk=1&mode=memory", - }, - Schema: "main", - }, - }, - Pending: []atlasexec.File{ - { - Name: "20221108173626.sql", - Version: "20221108173626", - }, - { - Name: "20221108173658.sql", - Version: "20221108173658", - }, - }, - Applied: []*atlasexec.AppliedFile{ - { - File: atlasexec.File{ - Name: "20221108173626.sql", - Version: "20221108173626", - }, - Start: must(time.Parse(time.RFC3339, "2024-06-16T15:27:38.914578+03:00")), - End: must(time.Parse(time.RFC3339, "2024-06-16T15:27:38.940343+03:00")), - Applied: []string{ - "CREATE TABLE `dept_emp_latest_date` (`emp_no` int NOT NULL, `from_date` date NULL, `to_date` date NULL) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT \"VIEW\";", - "CREATE TABLE `employees` (`emp_no` int NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` enum('M','F') NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`)) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci;", - }, - }, - { - File: atlasexec.File{ - Name: "20221108173658.sql", - Version: "20221108173658", - }, - Start: must(time.Parse(time.RFC3339, "2024-06-16T15:27:38.940343+03:00")), - End: must(time.Parse(time.RFC3339, "2024-06-16T15:27:38.963743+03:00")), - Applied: []string{ - "CREATE TABLE `employees` (`emp_no` int NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` enum('M','F') NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`)) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci;", - }, - }, - }, - Target: "20221108173658", - Start: must(time.Parse(time.RFC3339, "2024-06-16T15:27:38.909446+03:00")), - End: must(time.Parse(time.RFC3339, "2024-06-16T15:27:38.963743+03:00")), - }, - // language=markdown - expected: "

Migration Passed

atlas migrate apply Summary:

\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
ParameterDetails
Migration Directorytestdata/migrations
Database URLsqlite://file?_fk=1&mode=memory
Migrate to Version\n 20221108173658\n
SQL Summary2 migration files, 3 statements passed
Total Time54.297ms

Version 20221108173626.sql:

\n\n \n \n \n \n \n \n \n \n \n
StatusExecuted StatementsExecution TimeErrorError Statement
225.765ms--
📄 View SQL Statements\n\n```sql\nCREATE TABLE `dept_emp_latest_date` (`emp_no` int NOT NULL, `from_date` date NULL, `to_date` date NULL) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT \"VIEW\";\nCREATE TABLE `employees` (`emp_no` int NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` enum('M','F') NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`)) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci;\n```\n\n

Version 20221108173658.sql:

\n\n \n \n \n \n \n \n \n \n \n
StatusExecuted StatementsExecution TimeErrorError Statement
123.4ms--
📄 View SQL Statements\n\n```sql\nCREATE TABLE `employees` (`emp_no` int NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` enum('M','F') NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`)) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci;\n```\n\n
", - }, - { - name: "2 files, 1 statement error", - payload: &atlasexec.MigrateApply{ - Env: atlasexec.Env{ - Driver: "mysql", - Dir: "testdata/migrations", - URL: &sqlclient.URL{ - URL: &url.URL{ - Scheme: "mysql", - Host: "localhost:3306", - Path: "/test", - RawQuery: "parseTime=true", - }, - Schema: "test", - }, - }, - Pending: []atlasexec.File{ - { - Name: "20221108173626.sql", - Version: "20221108173626", - }, - { - Name: "20221108173658.sql", - Version: "20221108173658", - }, - }, - Applied: []*atlasexec.AppliedFile{ - { - File: atlasexec.File{ - Name: "20221108173626.sql", - Version: "20221108173626", - }, - Start: must(time.Parse(time.RFC3339, "2024-06-16T15:27:38.914578+03:00")), - End: must(time.Parse(time.RFC3339, "2024-06-16T15:27:38.940343+03:00")), - Applied: []string{ - "CREATE TABLE Persons ( PersonID int );", - }, - }, - { - File: atlasexec.File{ - Name: "20221108173658.sql", - Version: "20221108173658", - }, - Start: must(time.Parse(time.RFC3339, "2024-06-16T15:27:38.940343+03:00")), - End: must(time.Parse(time.RFC3339, "2024-06-16T15:27:38.963743+03:00")), - Applied: []string{ - "create Table Err?", - }, - Error: &struct { - Stmt string - Text string - }{ - Stmt: "create Table Err?", - Text: "Error 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?' at line 1", - }, - }, - }, - Current: "20221108143624", - Target: "20221108173658", - Start: must(time.Parse(time.RFC3339, "2024-06-16T15:27:38.909446+03:00")), - End: must(time.Parse(time.RFC3339, "2024-06-16T15:27:38.963743+03:00")), - Error: "sql/migrate: executing statement \"create Table Err?\" from version \"20240616125213\": Error 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?' at line 1", - }, - // language=markdown - expected: "

Migration Failed

atlas migrate apply Summary:

\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
ParameterDetails
Migration Directorytestdata/migrations
Database URLmysql://localhost:3306/test?parseTime=true
Migrate from Version20221108143624
Migrate to Version\n 20221108173658\n
SQL Summary2 migration files, 2 statements passed, 1 failed
Total Time54.297ms

Version 20221108173626.sql:

\n\n \n \n \n \n \n \n \n \n \n
StatusExecuted StatementsExecution TimeErrorError Statement
125.765ms--
📄 View SQL Statements\n\n```sql\nCREATE TABLE Persons ( PersonID int );\n```\n\n

Version 20221108173658.sql:

\n\n \n \n \n \n \n \n \n \n \n
StatusExecuted StatementsExecution TimeErrorError Statement
123.4msError 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?' at line 1
📄 View\n\n```sql\ncreate Table Err?\n```\n\n
📄 View SQL Statements\n\n```sql\ncreate Table Err?\n```\n\n
", - }, - { - name: "no work migration", - payload: &atlasexec.MigrateApply{ - Env: atlasexec.Env{ - Driver: "mysql", - Dir: "testdata/migrations", - URL: &sqlclient.URL{ - URL: &url.URL{ - Scheme: "mysql", - Host: "localhost:3306", - Path: "/test", - RawQuery: "parseTime=true", - }, - Schema: "test", - }, - }, - Current: "20240616130838", - Start: must(time.Parse(time.RFC3339, "2024-06-16T16:09:01.683771+03:00")), - End: must(time.Parse(time.RFC3339, "2024-06-16T16:09:01.689411+03:00")), - }, - expected: "

Migration Passed

atlas migrate apply Summary:

\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
ParameterDetails
Migration Directorytestdata/migrations
Database URLmysql://localhost:3306/test?parseTime=true
Migrate from Version20240616130838
Migrate to Version\n 20240616130838\n
SQL Summary0 migration files
Total Time5.64ms
", - }, - } { - t.Run(tt.name, func(t *testing.T) { - c, err := atlasaction.RenderTemplate("migrate-apply.tmpl", tt.payload) - require.NoError(t, err) - require.Equal(t, tt.expected, c) - }) - } -} - func generateHCL(t *testing.T, url, token string) string { st := fmt.Sprintf( `atlas { @@ -2538,6 +2139,48 @@ func (m *mockSCM) comment(_ context.Context, _ *atlasaction.PullRequest, id stri return err } +// Why another testscript for templates? +// Because I love to see the output in its full glory. +// Instead of a mess of quotes and escapes, I can see the actual output. +// It also allows to take output from atlas-cli and use it as input for the test. +// +// ```shell +// +// atlas --format "{{ json . }}" +// cp stdout stdin +// render template.md stdin +// cmp stdout expected.md +// +// ``` +func TestRenderTemplates(t *testing.T) { + testscript.Run(t, testscript.Params{ + Dir: "testdata/templates", + Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){ + "render-schema-plan": renderTemplate[*atlasexec.SchemaPlan], + "render-lint": renderTemplate[*atlasexec.SummaryReport], + "render-migrate-apply": renderTemplate[*atlasexec.MigrateApply], + }, + }) +} + +func renderTemplate[T any](ts *testscript.TestScript, neg bool, args []string) { + var data T + if neg { + ts.Fatalf("render commands should not fail") + } + if len(args) != 2 { + ts.Fatalf("usage: render-