Skip to content

Commit

Permalink
chore: fix some long running E2E tests (aws-amplify#2508)
Browse files Browse the repository at this point in the history
* chore: split some e2es

* test: update splits and re-run
  • Loading branch information
phani-srikar authored Apr 29, 2024
1 parent 4bfda10 commit 292264d
Show file tree
Hide file tree
Showing 10 changed files with 1,161 additions and 907 deletions.
830 changes: 443 additions & 387 deletions codebuild_specs/e2e_workflow.yml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const DURATION_20_MINUTES = 20 * ONE_MINUTE;
export const DURATION_30_MINUTES = 30 * ONE_MINUTE;
export const DURATION_45_MINUTES = 45 * ONE_MINUTE;
export const DURATION_1_HOUR = 60 * ONE_MINUTE;
export const DURATION_90_MINUTES = 90 * ONE_MINUTE;

export const COUNT_1_THOUSAND = 1000;
export const COUNT_10_THOUSAND = 10000;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { createNewProjectDir, deleteProjectDir } from 'amplify-category-api-e2e-
import { AmplifyGraphqlApi } from '@aws-amplify/graphql-api-construct';
import { initCDKProject, cdkDeploy, cdkDestroy } from '../../commands';
import { ValidateGraphqlOptions, validateGraphql } from '../../graphql-request';
import { DURATION_1_HOUR } from './deploy-velocity-constants';
import { DURATION_90_MINUTES } from './deploy-velocity-constants';

jest.setTimeout(DURATION_1_HOUR);
jest.setTimeout(DURATION_90_MINUTES);

export type EndpointConfig = Pick<ValidateGraphqlOptions, 'apiEndpoint' | 'apiKey'>;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
COUNT_100_THOUSAND,
DURATION_30_MINUTES,
DURATION_45_MINUTES,
MUTATION_FOUR_FIELD_CREATE,
SCHEMA_FOUR_FIELDS_FINAL_TWO_INDEXED,
SCHEMA_FOUR_FIELDS_INITIAL_TWO_INDEXED,
Expand All @@ -9,7 +9,7 @@ import { recordCountDataProvider, recordCountDataValidator, testManagedTableDepl

testManagedTableDeployment({
name: 'Replace 2 GSIs updated - 100k Records',
maxDeployDurationMs: DURATION_30_MINUTES,
maxDeployDurationMs: DURATION_45_MINUTES,
initialSchema: SCHEMA_FOUR_FIELDS_INITIAL_TWO_INDEXED,
updatedSchema: SCHEMA_FOUR_FIELDS_FINAL_TWO_INDEXED,
dataSetup: recordCountDataProvider(COUNT_100_THOUSAND, MUTATION_FOUR_FIELD_CREATE),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/* eslint-disable import/namespace */
import { DDB_AMPLIFY_MANAGED_DATASOURCE_STRATEGY } from '@aws-amplify/graphql-transformer-core';
import { createNewProjectDir, deleteProjectDir } from 'amplify-category-api-e2e-core';
import * as fs from 'fs-extra';
import * as generator from 'generate-password';
import * as path from 'path';
import { cdkDeploy, cdkDestroy, initCDKProject } from '../../../commands';
import { SqlDatabaseDetails, SqlDatatabaseController } from '../../../sql-datatabase-controller';
import { TestDefinition, dbDetailsToModelDataSourceStrategy, writeStackPrefix, writeTestDefinitions } from '../../../utils';
import {
testPrimaryContainsAssociated,
testPrimaryCpkSkOneContainsAssociated,
testPrimaryCpkSkTwoContainAssociated,
testPrimaryMultipleHasOneContainsOneHasOne,
testRelatedManyContainsAssociated,
testRelatedManyCpkSkOneContainsAssociated,
testRelatedManypkSkTwoContainsAssociated,
testRelatedOneContainsAssociated,
testRelatedOneCpkSkOneContainsAssociated,
testRelatedOneCpkSkTwoContainsAssociated,
} from './test-implementations';

jest.setTimeout(1000 * 60 * 60 /* 1 hour */);

describe('References relationships', () => {
const region = process.env.CLI_REGION ?? 'us-west-2';
const baseProjFolderName = path.basename(__filename, '.test.ts');

describe('DDB Primary, DDB Related', () => {
const projFolderName = `${baseProjFolderName}-ddb-primary-ddb-related`;
let apiEndpoint: string;
let apiKey: string;
let projRoot: string;
let currentId: number;

beforeEach(() => {
currentId = Date.now();
});

beforeAll(async () => {
projRoot = await createNewProjectDir(projFolderName);
const templatePath = path.resolve(path.join(__dirname, '..', '..', 'backends', 'configurable-sandbox-stack'));
const name = await initCDKProject(projRoot, templatePath);

const primarySchemaPath = path.resolve(path.join(__dirname, 'graphql', 'schema-primary.graphql'));
const primarySchema = fs.readFileSync(primarySchemaPath).toString();

const relatedSchemaPath = path.resolve(path.join(__dirname, 'graphql', 'schema-related.graphql'));
const relatedSchema = fs.readFileSync(relatedSchemaPath).toString();

const primarySchemaOneSkPath = path.resolve(path.join(__dirname, 'graphql', 'schema-primary-cpk-1sk.graphql'));
const primarySchemaOneSk = fs.readFileSync(primarySchemaOneSkPath).toString();

const relatedSchemaOneSkPath = path.resolve(path.join(__dirname, 'graphql', 'schema-related-cpk-1sk.graphql'));
const relatedSchemaOneSk = fs.readFileSync(relatedSchemaOneSkPath).toString();

const primarySchemaTwoSkPath = path.resolve(path.join(__dirname, 'graphql', 'schema-primary-cpk-2sk.graphql'));
const primarySchemaTwoSk = fs.readFileSync(primarySchemaTwoSkPath).toString();

const relatedSchemaTwoSkPath = path.resolve(path.join(__dirname, 'graphql', 'schema-related-cpk-2sk.graphql'));
const relatedSchemaTwoSk = fs.readFileSync(relatedSchemaTwoSkPath).toString();

const testDefinitions: Record<string, TestDefinition> = {
'ddb-primary-ddb-related': {
schema: [primarySchema, relatedSchema, primarySchemaOneSk, relatedSchemaOneSk, primarySchemaTwoSk, relatedSchemaTwoSk].join('\n'),
strategy: DDB_AMPLIFY_MANAGED_DATASOURCE_STRATEGY,
},
};

writeStackPrefix('RefDdbDdb', projRoot);
writeTestDefinitions(testDefinitions, projRoot);

const outputs = await cdkDeploy(projRoot, '--all');
apiEndpoint = outputs[name].awsAppsyncApiEndpoint;
apiKey = outputs[name].awsAppsyncApiKey;
});

afterAll(async () => {
try {
await cdkDestroy(projRoot, '--all');
} catch (err) {
console.log(`Error invoking 'cdk destroy': ${err}`);
}

deleteProjectDir(projRoot);
});

describe('Primary as source', () => {
test('Associated models included in query and mutation response with single primary key', async () => {
await testPrimaryContainsAssociated(currentId, apiEndpoint, apiKey);
});

test('Associated models included in query and mutation response with one sort key', async () => {
await testPrimaryCpkSkOneContainsAssociated(currentId, apiEndpoint, apiKey);
});

test('Associated models included in query and mutation response with two sort keys', async () => {
await testPrimaryCpkSkTwoContainAssociated(currentId, apiEndpoint, apiKey);
});

test('Multiple RelatedOne associated records returns one RelatedOne record', async () => {
await testPrimaryMultipleHasOneContainsOneHasOne(currentId, apiEndpoint, apiKey);
});
});

describe('RelatedOne as source', () => {
test('Associated models included in query and mutation response with single primary key', async () => {
await testRelatedOneContainsAssociated(currentId, apiEndpoint, apiKey);
});

test('Associated models included in query and mutation response with one sort key', async () => {
await testRelatedOneCpkSkOneContainsAssociated(currentId, apiEndpoint, apiKey);
});

test('Associated models included in query and mutation response with two sort keys', async () => {
await testRelatedOneCpkSkTwoContainsAssociated(currentId, apiEndpoint, apiKey);
});
});

describe('RelatedMany as source', () => {
test('Associated models included in query and mutation response with single primary key', async () => {
await testRelatedManyContainsAssociated(currentId, apiEndpoint, apiKey);
});

test('Associated models included in query and mutation response with one sort key', async () => {
await testRelatedManyCpkSkOneContainsAssociated(currentId, apiEndpoint, apiKey);
});

test('Associated models included in query and mutation response with two sort keys', async () => {
await testRelatedManypkSkTwoContainsAssociated(currentId, apiEndpoint, apiKey);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/* eslint-disable import/namespace */
import { DDB_AMPLIFY_MANAGED_DATASOURCE_STRATEGY } from '@aws-amplify/graphql-transformer-core';
import { createNewProjectDir, deleteProjectDir } from 'amplify-category-api-e2e-core';
import * as fs from 'fs-extra';
import * as generator from 'generate-password';
import * as path from 'path';
import { cdkDeploy, cdkDestroy, initCDKProject } from '../../../commands';
import { SqlDatabaseDetails, SqlDatatabaseController } from '../../../sql-datatabase-controller';
import { TestDefinition, dbDetailsToModelDataSourceStrategy, writeStackPrefix, writeTestDefinitions } from '../../../utils';
import {
testPrimaryContainsAssociated,
testPrimaryCpkSkOneContainsAssociated,
testPrimaryCpkSkTwoContainAssociated,
testPrimaryMultipleHasOneContainsOneHasOne,
testRelatedManyContainsAssociated,
testRelatedManyCpkSkOneContainsAssociated,
testRelatedManypkSkTwoContainsAssociated,
testRelatedOneContainsAssociated,
testRelatedOneCpkSkOneContainsAssociated,
testRelatedOneCpkSkTwoContainsAssociated,
} from './test-implementations';

jest.setTimeout(1000 * 60 * 60 /* 1 hour */);

describe('References relationships', () => {
const region = process.env.CLI_REGION ?? 'us-west-2';
const baseProjFolderName = path.basename(__filename, '.test.ts');

const [dbUsername, dbIdentifier] = generator.generateMultiple(2);
const dbname = 'default_db';
let dbDetails: SqlDatabaseDetails;

// Note that the SQL database is created with slightly non-standard naming conventions, to avoid us having to use `refersTo` in the schema
// snippets. That allows us to reuse the same snippets across both DDB and SQL data sources, simplifying the test fixture data.
const databaseController = new SqlDatatabaseController(
[
'drop table if exists `RelatedMany`;',
'drop table if exists `RelatedOne`;',
'drop table if exists `Primary`;',

// Single primary key
'create table `Primary` ( id varchar(64) primary key not null );',

'create table `RelatedMany`( id varchar(64) primary key not null, `primaryId` varchar(64));',
'create index `RelatedMany_primaryId` on `RelatedMany`(`primaryId`);',

'create table `RelatedOne`( id varchar(64) primary key not null, `primaryId` varchar(64));',
'create index `RelatedOne_primaryId` on `RelatedOne`(`primaryId`);',

// Two primary keys
'create table `PrimaryCPKSKOne` ( id varchar(64) not null, skOne varchar(64) not null, PRIMARY KEY (`id`, `skOne`) );',

'create table `RelatedManyCPKSKOne`( id varchar(64) primary key not null, `primaryId` varchar(64), `primarySkOne` varchar(64) );',
'create index `RelatedManyCPKSKOne_primaryId_skOne` on `RelatedManyCPKSKOne`(`primaryId`, `primarySkOne`);',

'create table `RelatedOneCPKSKOne`( id varchar(64) primary key not null, `primaryId` varchar(64), `primarySkOne` varchar(64) );',
'create index `RelatedOnCPKSKOne_primaryId_skOne` on `RelatedOneCPKSKOne`(`primaryId`, `primarySkOne`);',

// Three primary keys
'create table `PrimaryCPKSKTwo` ( id varchar(64) not null, skOne varchar(64) not null, skTwo varchar(64) not null, PRIMARY KEY (`id`, `skOne`, `skTwo`) );',

'create table `RelatedManyCPKSKTwo`( id varchar(64) primary key not null, `primaryId` varchar(64), `primarySkOne` varchar(64), `primarySkTwo` varchar(64) );',
'create index `RelatedManyCPKSKTwo` on `RelatedManyCPKSKTwo`(`primaryId`, `primarySkOne`, `primarySkTwo`);',

'create table `RelatedOneCPKSKTwo`( id varchar(64) primary key not null, `primaryId` varchar(64), `primarySkOne` varchar(64), `primarySkTwo` varchar(64) );',
'create index `RelatedOnCPKSKTwo_primaryId_skOne` on `RelatedOneCPKSKTwo`(`primaryId`, `primarySkOne`, `primarySkTwo`);',
],
{
identifier: dbIdentifier,
engine: 'mysql',
dbname,
username: dbUsername,
region,
},
);

beforeAll(async () => {
dbDetails = await databaseController.setupDatabase();
});

afterAll(async () => {
await databaseController.cleanupDatabase();
});

describe('DDB Primary, SQL Related', () => {
const projFolderName = `${baseProjFolderName}-ddb-primary-sql-related`;
let apiEndpoint: string;
let apiKey: string;
let projRoot: string;
let currentId: number;

beforeEach(() => {
currentId = Date.now();
});

beforeAll(async () => {
projRoot = await createNewProjectDir(projFolderName);
const templatePath = path.resolve(path.join(__dirname, '..', '..', 'backends', 'configurable-sandbox-stack'));
const name = await initCDKProject(projRoot, templatePath);

const primarySchemaPath = path.resolve(path.join(__dirname, 'graphql', 'schema-primary.graphql'));
const primarySchema = fs.readFileSync(primarySchemaPath).toString();

const relatedSchemaPath = path.resolve(path.join(__dirname, 'graphql', 'schema-related.graphql'));
const relatedSchema = fs.readFileSync(relatedSchemaPath).toString();

const primarySchemaOneSkPath = path.resolve(path.join(__dirname, 'graphql', 'schema-primary-cpk-1sk.graphql'));
const primarySchemaOneSk = fs.readFileSync(primarySchemaOneSkPath).toString();

const relatedSchemaOneSkPath = path.resolve(path.join(__dirname, 'graphql', 'schema-related-cpk-1sk.graphql'));
const relatedSchemaOneSk = fs.readFileSync(relatedSchemaOneSkPath).toString();

const primarySchemaTwoSkPath = path.resolve(path.join(__dirname, 'graphql', 'schema-primary-cpk-2sk.graphql'));
const primarySchemaTwoSk = fs.readFileSync(primarySchemaTwoSkPath).toString();

const relatedSchemaTwoSkPath = path.resolve(path.join(__dirname, 'graphql', 'schema-related-cpk-2sk.graphql'));
const relatedSchemaTwoSk = fs.readFileSync(relatedSchemaTwoSkPath).toString();

const testDefinitions: Record<string, TestDefinition> = {
'ddb-primary': {
schema: [primarySchema, primarySchemaOneSk, primarySchemaTwoSk].join('\n'),
strategy: DDB_AMPLIFY_MANAGED_DATASOURCE_STRATEGY,
},
'sql-related': {
schema: [relatedSchema, relatedSchemaOneSk, relatedSchemaTwoSk].join('\n'),
strategy: dbDetailsToModelDataSourceStrategy(dbDetails, 'sqlrelated', 'MYSQL', 'secretsManagerManagedSecret'),
},
};

writeStackPrefix('RefSqlDdb', projRoot);
writeTestDefinitions(testDefinitions, projRoot);

const outputs = await cdkDeploy(projRoot, '--all');
apiEndpoint = outputs[name].awsAppsyncApiEndpoint;
apiKey = outputs[name].awsAppsyncApiKey;
});

afterAll(async () => {
try {
await cdkDestroy(projRoot, '--all');
} catch (err) {
console.log(`Error invoking 'cdk destroy': ${err}`);
}

deleteProjectDir(projRoot);
});

describe('Primary as source', () => {
test('Associated models included in query and mutation response with single primary key', async () => {
await testPrimaryContainsAssociated(currentId, apiEndpoint, apiKey);
});

test('Associated models included in query and mutation response with one sort key', async () => {
await testPrimaryCpkSkOneContainsAssociated(currentId, apiEndpoint, apiKey);
});

test('Associated models included in query and mutation response with two sort keys', async () => {
await testPrimaryCpkSkTwoContainAssociated(currentId, apiEndpoint, apiKey);
});

test('Multiple RelatedOne associated records returns one RelatedOne record', async () => {
await testPrimaryMultipleHasOneContainsOneHasOne(currentId, apiEndpoint, apiKey);
});
});

describe('RelatedOne as source', () => {
test('Associated models included in query and mutation response with single primary key', async () => {
await testRelatedOneContainsAssociated(currentId, apiEndpoint, apiKey);
});

test('Associated models included in query and mutation response with one sort key', async () => {
await testRelatedOneCpkSkOneContainsAssociated(currentId, apiEndpoint, apiKey);
});

test('Associated models included in query and mutation response with two sort keys', async () => {
await testRelatedOneCpkSkTwoContainsAssociated(currentId, apiEndpoint, apiKey);
});
});

describe('RelatedMany as source', () => {
test('Associated models included in query and mutation response with single primary key', async () => {
await testRelatedManyContainsAssociated(currentId, apiEndpoint, apiKey);
});

test('Associated models included in query and mutation response with one sort key', async () => {
await testRelatedManyCpkSkOneContainsAssociated(currentId, apiEndpoint, apiKey);
});

test('Associated models included in query and mutation response with two sort keys', async () => {
await testRelatedManypkSkTwoContainsAssociated(currentId, apiEndpoint, apiKey);
});
});
});
});
Loading

0 comments on commit 292264d

Please sign in to comment.