Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export default defineConfig([
rules: { 'no-relative-import-paths/no-relative-import-paths': 2 },
},
{
files: ['scripts/**'],
files: ['scripts/**', '**/__test__/**/*.ts', '**/__tests__/**/*.ts'],
rules: {
'import-x/no-extraneous-dependencies': [
'error',
Expand Down Expand Up @@ -229,8 +229,8 @@ export default defineConfig([
'no-await-in-loop': 0,
'no-plusplus': [2, { allowForLoopAfterthoughts: true }],
'unicorn/prefer-top-level-await': 0,
'import-x/no-relative-packages': 1,
'no-relative-import-paths/no-relative-import-paths': 1
'import-x/no-relative-packages': 0,
'no-relative-import-paths/no-relative-import-paths': 0
},
},
]);
32 changes: 16 additions & 16 deletions internal/datastore/jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Config } from 'jest';
import type { Config } from "jest";

export const baseJestConfig: Config = {
preset: 'ts-jest',
preset: "ts-jest",

// Automatically clear mock calls, instances, contexts and results before every test
clearMocks: true,
Expand All @@ -10,14 +10,14 @@ export const baseJestConfig: Config = {
collectCoverage: true,

// The directory where Jest should output its coverage files
coverageDirectory: './.reports/unit/coverage',
coverageDirectory: "./.reports/unit/coverage",

// Indicates which provider should be used to instrument code for coverage
coverageProvider: 'babel',
coverageProvider: "babel",

// Module name mapper to handle TypeScript path aliases
moduleNameMapper: {
'^@internal/helpers$': '<rootDir>/../helpers/src'
"^@internal/helpers$": "<rootDir>/../helpers/src",
},

coverageThreshold: {
Expand All @@ -29,36 +29,36 @@ export const baseJestConfig: Config = {
},
},

coveragePathIgnorePatterns: ['/__tests__/'],
transform: { '^.+\\.ts$': 'ts-jest' },
testPathIgnorePatterns: ['.build'],
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
coveragePathIgnorePatterns: ["/__tests__/"],
transform: { "^.+\\.ts$": "ts-jest" },
testPathIgnorePatterns: [".build"],
testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"],

// Use this configuration option to add custom reporters to Jest
reporters: [
'default',
"default",
[
'jest-html-reporter',
"jest-html-reporter",
{
pageTitle: 'Test Report',
outputPath: './.reports/unit/test-report.html',
pageTitle: "Test Report",
outputPath: "./.reports/unit/test-report.html",
includeFailureMsg: true,
},
],
],

// The test environment that will be used for testing
testEnvironment: 'jsdom',
testEnvironment: "jsdom",
};

const utilsJestConfig = {
...baseJestConfig,

testEnvironment: 'node',
testEnvironment: "node",

coveragePathIgnorePatterns: [
...(baseJestConfig.coveragePathIgnorePatterns ?? []),
'zod-validators.ts',
"zod-validators.ts",
],
};

Expand Down
7 changes: 4 additions & 3 deletions internal/datastore/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"@aws-sdk/lib-dynamodb": "^3.858.0",
"@internal/helpers": "*",
"pino": "^9.7.0",
"zod": "^4.1.11"
"uuid": "^9.0.0",
"zod": "^4.1.11",
"zod-mermaid": "^1.0.9"
},
"devDependencies": {
"@stylistic/eslint-plugin": "^3.1.0",
Expand All @@ -19,8 +21,7 @@
"testcontainers": "^11.4.0",
"ts-jest": "^29.4.0",
"ts-node": "^10.9.2",
"typescript": "^5.9.3",
"zod-mermaid": "^1.0.9"
"typescript": "^5.9.3"
},
"license": "MIT",
"main": "src/index.ts",
Expand Down
190 changes: 97 additions & 93 deletions internal/datastore/src/__test__/db.ts
Original file line number Diff line number Diff line change
@@ -1,123 +1,122 @@
import { GenericContainer } from 'testcontainers';
import { GenericContainer } from "testcontainers";
import {
CreateTableCommand,
DeleteTableCommand,
DynamoDBClient,
UpdateTimeToLiveCommand
} from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
import { DatastoreConfig } from '../config';
UpdateTimeToLiveCommand,
} from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
import { DatastoreConfig } from "../config";

export async function setupDynamoDBContainer() {
const container = await new GenericContainer('amazon/dynamodb-local')
const container = await new GenericContainer("amazon/dynamodb-local")
.withExposedPorts(8000)
.start();

const endpoint = `http://${container.getHost()}:${container.getMappedPort(8000)}`;

const ddbClient = new DynamoDBClient({
region: 'us-west-2',
region: "us-west-2",
endpoint,
credentials: {
accessKeyId: 'fakeMyKeyId',
secretAccessKey: 'fakeSecretAccessKey'
}
accessKeyId: "fakeMyKeyId",
secretAccessKey: "fakeSecretAccessKey",
},
});

const docClient = DynamoDBDocumentClient.from(ddbClient);

const config : DatastoreConfig = {
region: 'us-west-2',
const config: DatastoreConfig = {
region: "us-west-2",
endpoint,
lettersTableName: 'letters',
miTableName: 'management-info',
suppliersTableName: 'suppliers',
lettersTableName: "letters",
miTableName: "management-info",
suppliersTableName: "suppliers",
lettersTtlHours: 1,
miTtlHours: 1
miTtlHours: 1,
};

return {
container,
ddbClient,
docClient,
endpoint,
config
config,
};
}

export type DBContext = Awaited<ReturnType<typeof setupDynamoDBContainer>>;

const createLetterTableCommand = new CreateTableCommand({
TableName: 'letters',
BillingMode: 'PAY_PER_REQUEST',
KeySchema: [
{ AttributeName: 'supplierId', KeyType: 'HASH' }, // Partition key
{ AttributeName: 'id', KeyType: 'RANGE' } // Sort key
],
GlobalSecondaryIndexes: [
{
IndexName: 'supplierStatus-index',
KeySchema: [
{ AttributeName: 'supplierStatus', KeyType: 'HASH' }, // Partition key for GSI
{ AttributeName: 'supplierStatusSk', KeyType: 'RANGE' } // Sort key for GSI
],
Projection: {
ProjectionType: 'ALL'
}
}
],
AttributeDefinitions: [
{ AttributeName: 'supplierId', AttributeType: 'S' },
{ AttributeName: 'id', AttributeType: 'S' },
{ AttributeName: 'supplierStatus', AttributeType: 'S' },
{ AttributeName: 'supplierStatusSk', AttributeType: 'S' },
]
});
TableName: "letters",
BillingMode: "PAY_PER_REQUEST",
KeySchema: [
{ AttributeName: "supplierId", KeyType: "HASH" }, // Partition key
{ AttributeName: "id", KeyType: "RANGE" }, // Sort key
],
GlobalSecondaryIndexes: [
{
IndexName: "supplierStatus-index",
KeySchema: [
{ AttributeName: "supplierStatus", KeyType: "HASH" }, // Partition key for GSI
{ AttributeName: "supplierStatusSk", KeyType: "RANGE" }, // Sort key for GSI
],
Projection: {
ProjectionType: "ALL",
},
},
],
AttributeDefinitions: [
{ AttributeName: "supplierId", AttributeType: "S" },
{ AttributeName: "id", AttributeType: "S" },
{ AttributeName: "supplierStatus", AttributeType: "S" },
{ AttributeName: "supplierStatusSk", AttributeType: "S" },
],
});

const updateTimeToLiveCommand = new UpdateTimeToLiveCommand({
TableName: 'letters',
TimeToLiveSpecification: {
AttributeName: 'ttl',
Enabled: true
}
});
TableName: "letters",
TimeToLiveSpecification: {
AttributeName: "ttl",
Enabled: true,
},
});

const createMITableCommand = new CreateTableCommand({
TableName: 'management-info',
BillingMode: 'PAY_PER_REQUEST',
KeySchema: [
{ AttributeName: 'supplierId', KeyType: 'HASH' }, // Partition key
{ AttributeName: 'id', KeyType: 'RANGE' } // Sort key
],
AttributeDefinitions: [
{ AttributeName: 'supplierId', AttributeType: 'S' },
{ AttributeName: 'id', AttributeType: 'S' },
]
});
TableName: "management-info",
BillingMode: "PAY_PER_REQUEST",
KeySchema: [
{ AttributeName: "supplierId", KeyType: "HASH" }, // Partition key
{ AttributeName: "id", KeyType: "RANGE" }, // Sort key
],
AttributeDefinitions: [
{ AttributeName: "supplierId", AttributeType: "S" },
{ AttributeName: "id", AttributeType: "S" },
],
});

const createSupplierTableCommand = new CreateTableCommand({
TableName: 'suppliers',
BillingMode: 'PAY_PER_REQUEST',
KeySchema: [
{ AttributeName: 'id', KeyType: 'HASH' } // Partition key
],
GlobalSecondaryIndexes: [
{
IndexName: 'supplier-apim-index',
KeySchema: [
{ AttributeName: 'apimId', KeyType: 'HASH' } // Partition key for GSI
],
Projection: {
ProjectionType: 'ALL'
}
}
],
AttributeDefinitions: [
{ AttributeName: 'id', AttributeType: 'S' },
{ AttributeName: 'apimId', AttributeType: 'S' }
]
});

TableName: "suppliers",
BillingMode: "PAY_PER_REQUEST",
KeySchema: [
{ AttributeName: "id", KeyType: "HASH" }, // Partition key
],
GlobalSecondaryIndexes: [
{
IndexName: "supplier-apim-index",
KeySchema: [
{ AttributeName: "apimId", KeyType: "HASH" }, // Partition key for GSI
],
Projection: {
ProjectionType: "ALL",
},
},
],
AttributeDefinitions: [
{ AttributeName: "id", AttributeType: "S" },
{ AttributeName: "apimId", AttributeType: "S" },
],
});

export async function createTables(context: DBContext) {
const { ddbClient } = context;
Expand All @@ -129,19 +128,24 @@ export async function createTables(context: DBContext) {
await ddbClient.send(createSupplierTableCommand);
}


export async function deleteTables(context: DBContext) {
const { ddbClient } = context;

await ddbClient.send(new DeleteTableCommand({
TableName: 'letters'
}));

await ddbClient.send(new DeleteTableCommand({
TableName: 'management-info'
}));

await ddbClient.send(new DeleteTableCommand({
TableName: 'suppliers'
}));
await ddbClient.send(
new DeleteTableCommand({
TableName: "letters",
}),
);

await ddbClient.send(
new DeleteTableCommand({
TableName: "management-info",
}),
);

await ddbClient.send(
new DeleteTableCommand({
TableName: "suppliers",
}),
);
}
Loading