package changes import ( "os" "testing" "github.com/bluekeyes/go-gitdiff/gitdiff" "github.com/stretchr/testify/require" ) func getDiff(t *testing.T, diffDataFile string) []*gitdiff.File { t.Helper() patch, err := os.Open(diffDataFile) if err != nil { t.Fatal(err) } files, _, err := gitdiff.Parse(patch) if err != nil { t.Fatal(err) } return files } func Test_parseColumnChange(t *testing.T) { type args struct { line string } tests := []struct { name string args args wantName string wantDataType string wantColumnType columnType }{ {name: "Should parse name and data type when change is a column", args: args{line: "|name|String|"}, wantName: "name", wantDataType: "String"}, {name: "Should parse name, pk and data type when a column is a primary key", args: args{line: "|name (PK)|String|"}, wantName: "name", wantDataType: "String", wantColumnType: columnTypePK}, {name: "Should return empty strings when change is not a column", args: args{line: "# Table: azure_appservice_site_auth_settings"}, wantName: "", wantDataType: ""}, {name: "Should parse name, incremental key and data type when a column is an incremental key", args: args{line: "|updated_at (Incremental Key)|Timestamp|"}, wantName: "updated_at", wantDataType: "Timestamp", wantColumnType: columnTypeIncremental}, {name: "Should parse name, pk and incremental key", args: args{line: "|updated_at (PK) (Incremental Key)|Timestamp|"}, wantName: "updated_at", wantDataType: "Timestamp", wantColumnType: columnTypeIncremental | columnTypePK}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotName, gotCol := parseColumnChange(tt.args.line) require.Equal(t, tt.wantName, gotName) require.Equal(t, tt.wantDataType, gotCol.dataType) require.Equal(t, tt.wantColumnType, gotCol.columnType) }) } } func Test_parsePKChange(t *testing.T) { type args struct { line string } tests := []struct { name string args args wantNames []string }{ {name: "PK present", args: args{line: "The composite primary key for this table is (**org**, **id**, **hook_id**)."}, wantNames: []string{"org", "id", "hook_id"}}, {name: "no PK", args: args{}, wantNames: nil}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotNames := parsePKChange(tt.args.line) require.Equal(t, tt.wantNames, gotNames) }) } } func Test_getChanges(t *testing.T) { tests := []struct { name string diffDataFile string wantChanges []change wantErr bool }{ { name: "Should return breaking changes", diffDataFile: "testdata/pr_4768_diff.txt", wantChanges: []change{ { Text: "Table `azure_web_functions` was renamed to `azure_appservice_functions`", Breaking: true, }, { Text: "Table `azure_appservice_functions`: column `web_app_id` removed from table", Breaking: true, }, { Text: "Table `azure_appservice_functions`: column added with name `site_id` and type `String`", Breaking: false, }, { Text: "Table `azure_subscription_locations` was added", Breaking: false, }, { Text: "Table `azure_resources_links` was renamed to `azure_subscription_tenants`", Breaking: true, }, { Text: "Table `azure_subscription_tenants`: column `name` removed from table", Breaking: true, }, { Text: "Table `azure_subscription_tenants`: column `properties_notes` removed from table", Breaking: true, }, { Text: "Table `azure_subscription_tenants`: column `properties_source_id` removed from table", Breaking: true, }, { Text: "Table `azure_subscription_tenants`: column `properties_target_id` removed from table", Breaking: true, }, { Text: "Table `azure_subscription_tenants`: column added with name `tenant_id` and type `String`", Breaking: false, }, { Text: "Table `azure_subscriptions`: column `managed_by_tenants` removed from table", Breaking: true, }, { Text: "Table `azure_subscriptions`: column `tags` removed from table", Breaking: true, }, { Text: "Table `azure_subscriptions`: column `tenant_id` removed from table", Breaking: true, }, { Text: "Table `azure_subscriptions`: column added with name `subscription_id` and type `String`", Breaking: false, }, { Text: "Table `azure_web_publishing_profiles` was removed", Breaking: true, }, }, wantErr: false, }, { name: "Should handle PK changes", diffDataFile: "testdata/pr_5636_diff.txt", wantChanges: []change{ { Text: "Table `gcp_resourcemanager_projects`: primary key constraint added to column `name`", Breaking: true, }, { Text: "Table `gcp_resourcemanager_projects`: primary key constraint added to column `project_id`", Breaking: true, }, { Text: "Table `gcp_resourcemanager_projects`: primary key constraint removed from column `_cq_id`", Breaking: true, }, }, }, { name: "Should mark newly added PK as breaking", diffDataFile: "testdata/pr_5802_diff.txt", wantChanges: []change{ { Text: "Table `aws_ses_configuration_sets`: column added with name `arn (PK)` and type `String`", Breaking: true, }, { Text: "Table `aws_ses_configuration_sets`: primary key constraint removed from column `account_id`", Breaking: true, }, { Text: "Table `aws_ses_configuration_sets`: primary key constraint removed from column `name`", Breaking: true, }, { Text: "Table `aws_ses_configuration_sets`: primary key constraint removed from column `region`", Breaking: true, }, }, }, { name: "Should not report PK order change if they were other PK changes", diffDataFile: "testdata/pr_9677_diff.txt", wantChanges: []change{ { Text: "Table `aws_iam_users`: column `id` removed from table", Breaking: true, }, { Text: "Table `aws_iam_users`: primary key constraint added to column `arn`", Breaking: true, }, }, }, { name: "Should mark PK order change as breaking", diffDataFile: "testdata/pr_6012_diff.txt", wantChanges: []change{ { Text: "Table `github_hook_deliveries`: primary key order changed from `org, id, hook_id` to `org, hook_id, id`", Breaking: true, }, { Text: "Table `github_issues`: column added with name `state_reason` and type `String`", Breaking: false, }, { Text: "Table `github_repositories`: column added with name `has_discussions` and type `Bool`", Breaking: false, }, { Text: "Table `github_team_members`: primary key order changed from `org, id, team_id` to `org, team_id, id`", Breaking: true, }, { Text: "Table `github_team_repositories`: primary key order changed from `org, id, team_id` to `org, team_id, id`", Breaking: true, }, { Text: "Table `github_team_repositories`: column added with name `has_discussions` and type `Bool`", Breaking: false, }, }, }, { name: "Should handle incremental column changes", diffDataFile: "testdata/pr_6707_diff.txt", wantChanges: []change{ { Text: "Table `shopify_abandoned_checkouts`: column `updated_at` added to cursor for incremental syncs", Breaking: true, }, { Text: "Table `shopify_customers`: column `created_at` removed from cursor for incremental syncs", Breaking: true, }, { Text: "Table `shopify_customers`: column `updated_at` added to cursor for incremental syncs", Breaking: true, }, { Text: "Table `shopify_orders`: column `created_at` added to cursor for incremental syncs", Breaking: true, }, { Text: "Table `shopify_orders`: column `updated_at` added to cursor for incremental syncs", Breaking: true, }, { Text: "Table `shopify_price_rules`: column `updated_at` added to cursor for incremental syncs", Breaking: true, }, { Text: "Table `shopify_products`: column `updated_at` added to cursor for incremental syncs", Breaking: true, }, }, }, { name: "Special Case - don't report removal of pks, only end state", diffDataFile: "testdata/pr_100644_diff.txt", wantChanges: []change{ { Text: "Table `aws_accessanalyzer_analyzers`: all existing primary key constraints have been removed and a primary key new constraint has been added to `_cq_id`", Breaking: true, }, }, }, { name: "Should handle no backticks -> backticks", diffDataFile: "testdata/pr_11034_diff.txt", wantChanges: []change{}, }, { name: "Should handle newlines", diffDataFile: "testdata/pr_11259_diff.txt", wantChanges: []change{}, }, { name: "Should ignore removal of _cq_source_name and _cq_sync_time for SDK v4 migration", diffDataFile: "testdata/pr_11855_diff.txt", wantChanges: []change{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff := getDiff(t, tt.diffDataFile) gotChanges, err := GetChanges(diff) if tt.wantErr { require.Error(t, err) } require.Equal(t, tt.wantChanges, gotChanges) }) } }