-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🪟 🧪 E2E Tests for auto-detect schema changes (#20682)
* Add support to get workspaceId, create/delete source/destination from cypress * Test delete source * commands/request -> commands/api * Add util to convert chains to promises, cleanup api, use async/await on test * Add the ability to create and delete a connection * Add E2E test for non-breaking schema changes * Update db queries with generator functions * Update users table to include better columns and cursor * Add name to ResetWarningModalSwitch * Add test ID to review button in SchemaChangesDetected modal * Add test for breaking change auto-detect flow * Fix linting issues * Clean up afterEach for autoDetectSchema * schemaChangesButton should be disabled after clicking * Append random strings to source, dest, and connection created by auto-detect e2e test * Add E2E tests to verify status icon in list pages * Ensure that the manual sync button is enabled or disabled depending on the schema change type * Add test to verify non-breaking prefrence update * Fix testid prop case in StatusCell * Fix small issues in auto-detect schema tests * Remove it.only from autoDetectSchema.spec * Remove promisification of api requests in e2e * Fix typing in apiRequest * Cleanup requestWorkspaceId * Update auto-detect e2e test to check where backdrop should not exist for non-breaking changes * Remove promise util for cypress * Fix column name in connection e2e spec * Fix fields in connection spec to match the new users table. Add email records to users table
- Loading branch information
Showing
17 changed files
with
572 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { | ||
ConnectionGetBody, | ||
Connection, | ||
ConnectionCreateRequestBody, | ||
ConnectionsList, | ||
Destination, | ||
DestinationsList, | ||
Source, | ||
SourceDiscoverSchema, | ||
SourcesList, | ||
} from "./types"; | ||
import { getWorkspaceId, setWorkspaceId } from "./workspace"; | ||
|
||
const getApiUrl = (path: string): string => `http://localhost:8001/api/v1${path}`; | ||
|
||
const apiRequest = <T = void>( | ||
method: Cypress.HttpMethod, | ||
path: string, | ||
payload?: Cypress.RequestBody, | ||
expectedStatus = 200 | ||
): Cypress.Chainable<T> => | ||
cy.request(method, getApiUrl(path), payload).then((response) => { | ||
expect(response.status).to.eq(expectedStatus, "response status"); | ||
return response.body; | ||
}); | ||
|
||
export const requestWorkspaceId = () => | ||
apiRequest<{ workspaces: Array<{ workspaceId: string }> }>("POST", "/workspaces/list").then( | ||
({ workspaces: [{ workspaceId }] }) => { | ||
setWorkspaceId(workspaceId); | ||
} | ||
); | ||
|
||
export const requestConnectionsList = () => | ||
apiRequest<ConnectionsList>("POST", "/connections/list", { workspaceId: getWorkspaceId() }); | ||
|
||
export const requestCreateConnection = (body: ConnectionCreateRequestBody) => | ||
apiRequest<Connection>("POST", "/web_backend/connections/create", body); | ||
|
||
export const requestUpdateConnection = (body: Record<string, unknown>) => | ||
apiRequest<Connection>("POST", "/web_backend/connections/update", body); | ||
|
||
export const requestGetConnection = (body: ConnectionGetBody) => | ||
apiRequest<Connection>("POST", "/web_backend/connections/get", body); | ||
|
||
export const requestDeleteConnection = (connectionId: string) => | ||
apiRequest("POST", "/connections/delete", { connectionId }, 204); | ||
|
||
export const requestSourcesList = () => | ||
apiRequest<SourcesList>("POST", "/sources/list", { workspaceId: getWorkspaceId() }); | ||
|
||
export const requestSourceDiscoverSchema = (sourceId: string) => | ||
apiRequest<SourceDiscoverSchema>("POST", "/sources/discover_schema", { sourceId, disable_cache: true }); | ||
|
||
export const requestCreateSource = (body: Record<string, unknown>) => | ||
apiRequest<Source>("POST", "/sources/create", body); | ||
|
||
export const requestDeleteSource = (sourceId: string) => apiRequest("POST", "/sources/delete", { sourceId }, 204); | ||
|
||
export const requestDestinationsList = () => | ||
apiRequest<DestinationsList>("POST", "/destinations/list", { workspaceId: getWorkspaceId() }); | ||
|
||
export const requestCreateDestination = (body: Record<string, unknown>) => | ||
apiRequest<Destination>("POST", "/destinations/create", body); | ||
|
||
export const requestDeleteDestination = (destinationId: string) => | ||
apiRequest("POST", "/destinations/delete", { destinationId }, 204); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./api"; | ||
export * from "./payloads"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { ConnectionCreateRequestBody } from "./types"; | ||
import { getWorkspaceId } from "./workspace"; | ||
|
||
type RequiredConnectionCreateRequestProps = "name" | "sourceId" | "destinationId" | "syncCatalog" | "sourceCatalogId"; | ||
type CreationConnectRequestParams = Pick<ConnectionCreateRequestBody, RequiredConnectionCreateRequestProps> & | ||
Partial<Omit<ConnectionCreateRequestBody, RequiredConnectionCreateRequestProps>>; | ||
|
||
export const getConnectionCreateRequest = (params: CreationConnectRequestParams): ConnectionCreateRequestBody => ({ | ||
geography: "auto", | ||
namespaceDefinition: "source", | ||
namespaceFormat: "${SOURCE_NAMESPACE}", | ||
nonBreakingChangesPreference: "ignore", | ||
operations: [], | ||
prefix: "", | ||
scheduleType: "manual", | ||
status: "active", | ||
...params, | ||
}); | ||
|
||
export const getPostgresCreateSourceBody = (name: string) => ({ | ||
name, | ||
sourceDefinitionId: "decd338e-5647-4c0b-adf4-da0e75f5a750", | ||
workspaceId: getWorkspaceId(), | ||
connectionConfiguration: { | ||
ssl_mode: { mode: "disable" }, | ||
tunnel_method: { tunnel_method: "NO_TUNNEL" }, | ||
replication_method: { method: "Standard" }, | ||
ssl: false, | ||
port: 5433, | ||
schemas: ["public"], | ||
host: "localhost", | ||
database: "airbyte_ci_source", | ||
username: "postgres", | ||
password: "secret_password", | ||
}, | ||
}); | ||
|
||
export const getE2ETestingCreateDestinationBody = (name: string) => ({ | ||
name, | ||
workspaceId: getWorkspaceId(), | ||
destinationDefinitionId: "2eb65e87-983a-4fd7-b3e3-9d9dc6eb8537", | ||
connectionConfiguration: { | ||
type: "LOGGING", | ||
logging_config: { | ||
logging_type: "FirstN", | ||
max_entry_count: 100, | ||
}, | ||
}, | ||
}); | ||
|
||
export const getPostgresCreateDestinationBody = (name: string) => ({ | ||
name, | ||
workspaceId: getWorkspaceId(), | ||
destinationDefinitionId: "25c5221d-dce2-4163-ade9-739ef790f503", | ||
connectionConfiguration: { | ||
ssl_mode: { mode: "disable" }, | ||
tunnel_method: { tunnel_method: "NO_TUNNEL" }, | ||
ssl: false, | ||
port: 5434, | ||
schema: "public", | ||
host: "localhost", | ||
database: "airbyte_ci_destination", | ||
username: "postgres", | ||
password: "secret_password", | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
export interface Connection { | ||
connectionId: string; | ||
destination: Destination; | ||
destinationId: string; | ||
isSyncing: boolean; | ||
name: string; | ||
scheduleType: string; | ||
schemaChange: string; | ||
source: Source; | ||
sourceId: string; | ||
status: "active" | "inactive" | "deprecated"; | ||
nonBreakingChangesPreference: "ignore" | "disable"; | ||
syncCatalog: SyncCatalog; | ||
} | ||
|
||
export interface ConnectionCreateRequestBody { | ||
destinationId: string; | ||
geography: string; | ||
name: string; | ||
namespaceDefinition: string; | ||
namespaceFormat: string; | ||
nonBreakingChangesPreference: "ignore" | "disable"; | ||
operations: unknown[]; | ||
prefix: string; | ||
scheduleType: string; | ||
sourceCatalogId: string; | ||
sourceId: string; | ||
status: "active"; | ||
syncCatalog: SyncCatalog; | ||
} | ||
|
||
export interface ConnectionGetBody { | ||
connectionId: string; | ||
withRefreshedCatalog?: boolean; | ||
} | ||
|
||
export interface ConnectionsList { | ||
connections: Connection[]; | ||
} | ||
|
||
export interface Destination { | ||
destinationDefinitionId: string; | ||
destinationName: string; | ||
destinationId: string; | ||
connectionConfiguration: Record<string, unknown>; | ||
} | ||
|
||
export interface DestinationsList { | ||
destinations: Destination[]; | ||
} | ||
|
||
export interface Source { | ||
sourceDefinitionId: string; | ||
sourceName: string; | ||
sourceId: string; | ||
connectionConfiguration: Record<string, unknown>; | ||
} | ||
|
||
export interface SourceDiscoverSchema { | ||
catalog: SyncCatalog; | ||
catalogId: string; | ||
} | ||
|
||
export interface SourcesList { | ||
sources: Source[]; | ||
} | ||
|
||
export interface SyncCatalog { | ||
streams: SyncCatalogStream[]; | ||
} | ||
|
||
export interface SyncCatalogStream { | ||
config: Record<string, unknown>; | ||
stream: Record<string, unknown>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
let _workspaceId: string; | ||
|
||
export const setWorkspaceId = (workspaceId: string) => { | ||
_workspaceId = workspaceId; | ||
}; | ||
|
||
export const getWorkspaceId = () => { | ||
return _workspaceId; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,68 @@ | ||
export const createTable = (tableName: string, columns: string[]): string => | ||
`CREATE TABLE ${tableName}(${columns.join(", ")});`; | ||
|
||
export const dropTable = (tableName: string) => `DROP TABLE IF EXISTS ${tableName}`; | ||
|
||
export const alterTable = (tableName: string, params: { add?: string[]; drop?: string[] }): string => { | ||
const adds = params.add ? params.add.map((add) => `ADD COLUMN ${add}`) : []; | ||
const drops = params.drop ? params.drop.map((columnName) => `DROP COLUMN ${columnName}`) : []; | ||
const alterations = [...adds, ...drops]; | ||
|
||
return `ALTER TABLE ${tableName} ${alterations.join(", ")};`; | ||
}; | ||
|
||
export const insertIntoTable = (tableName: string, valuesByColumn: Record<string, unknown>): string => { | ||
const keys = Object.keys(valuesByColumn); | ||
const values = keys | ||
.map((key) => valuesByColumn[key]) | ||
.map((value) => (typeof value === "string" ? `'${value}'` : value)); | ||
|
||
return `INSERT INTO ${tableName}(${keys.join(", ")}) VALUES(${values.join(", ")});`; | ||
}; | ||
|
||
export const insertMultipleIntoTable = (tableName: string, valuesByColumns: Array<Record<string, unknown>>): string => | ||
valuesByColumns.map((valuesByColumn) => insertIntoTable(tableName, valuesByColumn)).join("\n"); | ||
|
||
// Users table | ||
export const createUsersTableQuery = ` | ||
CREATE TABLE users(id SERIAL PRIMARY KEY, col1 VARCHAR(200));`; | ||
export const insertUsersTableQuery = ` | ||
INSERT INTO public.users(col1) VALUES('record1'); | ||
INSERT INTO public.users(col1) VALUES('record2'); | ||
INSERT INTO public.users(col1) VALUES('record3');`; | ||
export const createUsersTableQuery = createTable("public.users", [ | ||
"id SERIAL", | ||
"name VARCHAR(200) NULL", | ||
"email VARCHAR(200) NULL", | ||
"updated_at TIMESTAMP", | ||
"CONSTRAINT users_pkey PRIMARY KEY (id)", | ||
]); | ||
export const insertUsersTableQuery = insertMultipleIntoTable("public.users", [ | ||
{ name: "Abigail", email: "[email protected]", updated_at: "2022-12-19 00:00:00" }, | ||
{ name: "Andrew", email: "[email protected]", updated_at: "2022-12-19 00:00:00" }, | ||
{ name: "Kat", email: "[email protected]", updated_at: "2022-12-19 00:00:00" }, | ||
]); | ||
|
||
export const dropUsersTableQuery = ` | ||
DROP TABLE IF EXISTS users;`; | ||
export const dropUsersTableQuery = dropTable("public.users"); | ||
|
||
// Cities table | ||
export const createCitiesTableQuery = ` | ||
CREATE TABLE cities(city_code VARCHAR(8), city VARCHAR(200));`; | ||
|
||
export const insertCitiesTableQuery = ` | ||
INSERT INTO public.cities(city_code, city) VALUES('BCN', 'Barcelona'); | ||
INSERT INTO public.cities(city_code, city) VALUES('MAD', 'Madrid'); | ||
INSERT INTO public.cities(city_code, city) VALUES('VAL', 'Valencia')`; | ||
|
||
export const alterCitiesTableQuery = ` | ||
ALTER TABLE public.cities | ||
DROP COLUMN "city_code", | ||
ADD COLUMN "state" text, | ||
ADD COLUMN "country" text;`; | ||
export const dropCitiesTableQuery = ` | ||
DROP TABLE IF EXISTS cities;`; | ||
export const createCitiesTableQuery = createTable("public.cities", ["city_code VARCHAR(8)", "city VARCHAR(200)"]); | ||
|
||
export const insertCitiesTableQuery = insertMultipleIntoTable("public.cities", [ | ||
{ | ||
city_code: "BCN", | ||
city: "Barcelona", | ||
}, | ||
{ city_code: "MAD", city: "Madrid" }, | ||
{ city_code: "VAL", city: "Valencia" }, | ||
]); | ||
|
||
export const alterCitiesTableQuery = alterTable("public.cities", { | ||
add: ["state TEXT", "country TEXT"], | ||
drop: ["city_code"], | ||
}); | ||
export const dropCitiesTableQuery = dropTable("public.cities"); | ||
|
||
// Cars table | ||
export const createCarsTableQuery = ` | ||
CREATE TABLE cars(id SERIAL PRIMARY KEY, mark VARCHAR(200), model VARCHAR(200), color VARCHAR(200));`; | ||
export const dropCarsTableQuery = ` | ||
DROP TABLE IF EXISTS cars;`; | ||
export const createCarsTableQuery = createTable("public.cars", [ | ||
"id SERIAL PRIMARY KEY", | ||
"mark VARCHAR(200)", | ||
"model VARCHAR(200)", | ||
"color VARCHAR(200)", | ||
]); | ||
|
||
export const dropCarsTableQuery = dropTable("public.cars"); |
Oops, something went wrong.