Skip to content

Commit

Permalink
Fixed failing SQLite e2e tests and TS config
Browse files Browse the repository at this point in the history
E2E tests were failing, as there was no nested database folder in the 'testing' subdirectory. Probably locally it was created, but git is not commiting empty folders. Adjusted not to require such nesting.

Fixed also TS config to compile SQLite package correctly, as some entries were missing. After that fixed the common errors.

Added a helper withConnection to streamline the connection management. Now it also uses correctly close in finalize, instead of double closing in case of error.

Renamed location to fileName to follow the SQLite naming convention, made it also optional with fallback to in memory.

Removed absolute file path and custom, as it won't allow easily passing the filenames without casting that are not typed manually.
  • Loading branch information
oskardudycz committed Feb 11, 2025
1 parent 4e79aeb commit 50da562
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 123 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import {
} from '@event-driven-io/emmett';
import fs from 'fs';
import { afterEach, describe, it } from 'node:test';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import path from 'path';
import { v4 as uuid } from 'uuid';
import { sqliteConnection, type AbsolutePath } from '../sqliteConnection';
import { InMemorySQLiteDatabase, sqliteConnection } from '../sqliteConnection';
import {
type DiscountApplied,
type PricedProductItem,
Expand All @@ -20,26 +19,21 @@ import {
import { createEventStoreSchema } from './schema';
import { getSQLiteEventStore } from './SQLiteEventStore';

const __dirname = dirname(fileURLToPath(import.meta.url)) as AbsolutePath;

void describe('SQLiteEventStore', () => {
const testDatabasePath: AbsolutePath = __dirname + '/../testing/database/';
const testDatabasePath = path.resolve(process.cwd(), 'src', 'testing');
const dbLocation = path.resolve(testDatabasePath, 'db.sqlite');

afterEach(() => {
if (!fs.existsSync(`${testDatabasePath}/test.db`)) {
if (!fs.existsSync(dbLocation)) {
return;
}
fs.unlink(`${testDatabasePath}/test.db`, (err) => {
if (err) console.error('Error deleting file:', err);
});
fs.unlinkSync(dbLocation);
});

void it('should append events', async () => {
await createEventStoreSchema(
sqliteConnection({ location: `/${testDatabasePath}/test.db` }),
);
await createEventStoreSchema(sqliteConnection({ fileName: dbLocation }));
const eventStore = getSQLiteEventStore({
databaseLocation: `${testDatabasePath}/test.db`,
fileName: `${testDatabasePath}/test.db`,
});

const productItem: PricedProductItem = {
Expand Down Expand Up @@ -80,10 +74,10 @@ void describe('SQLiteEventStore', () => {

void it('should aggregate stream', async () => {
await createEventStoreSchema(
sqliteConnection({ location: `${testDatabasePath}/test.db` }),
sqliteConnection({ fileName: `${testDatabasePath}/test.db` }),
);
const eventStore = getSQLiteEventStore({
databaseLocation: `${testDatabasePath}/test.db`,
fileName: `${testDatabasePath}/test.db`,
});

const productItem: PricedProductItem = {
Expand Down Expand Up @@ -132,7 +126,7 @@ void describe('SQLiteEventStore', () => {
schema: {
autoMigration: 'CreateOrUpdate',
},
databaseLocation: `${testDatabasePath}/test.db`,
fileName: `${testDatabasePath}/test.db`,
});

const productItem: PricedProductItem = {
Expand All @@ -158,7 +152,7 @@ void describe('SQLiteEventStore', () => {
schema: {
autoMigration: 'CreateOrUpdate',
},
databaseLocation: ':memory:',
fileName: InMemorySQLiteDatabase,
});
const productItem: PricedProductItem = {
productId: '123',
Expand All @@ -183,7 +177,7 @@ void describe('SQLiteEventStore', () => {
schema: {
autoMigration: 'CreateOrUpdate',
},
databaseLocation: `${testDatabasePath}/test.db`,
fileName: `${testDatabasePath}/test.db`,
});

const productItem: PricedProductItem = {
Expand All @@ -206,7 +200,7 @@ void describe('SQLiteEventStore', () => {
schema: {
autoMigration: 'CreateOrUpdate',
},
databaseLocation: `${testDatabasePath}/test.db`,
fileName: `${testDatabasePath}/test.db`,
});

const stream = await sameEventStore.readStream(shoppingCartId);
Expand All @@ -220,7 +214,7 @@ void describe('SQLiteEventStore', () => {
schema: {
autoMigration: 'CreateOrUpdate',
},
databaseLocation: `${testDatabasePath}/test.db`,
fileName: `${testDatabasePath}/test.db`,
});

const productItem: PricedProductItem = {
Expand Down
113 changes: 45 additions & 68 deletions src/packages/emmett-sqlite/src/eventStore/SQLiteEventStore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {
AppendToStreamResultWithGlobalPosition,
BigIntStreamPosition,
Event,
ReadEvent,
Expand All @@ -12,15 +13,13 @@ import {
type AggregateStreamOptions,
type AggregateStreamResult,
type AppendToStreamOptions,
type AppendToStreamResult,
type EventStore,
type ReadStreamOptions,
type ReadStreamResult,
} from '@event-driven-io/emmett';
import {
InMemorySQLiteDatabase,
sqliteConnection,
type AbsolutePath,
type RelativePath,
type SQLiteConnection,
} from '../sqliteConnection';
import { createEventStoreSchema } from './schema';
Expand All @@ -46,36 +45,52 @@ export type SQLiteEventStoreOptions = {
schema?: {
autoMigration?: 'None' | 'CreateOrUpdate';
};
databaseLocation: AbsolutePath | RelativePath | ':memory:';
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
fileName: InMemorySQLiteDatabase | string | undefined;
};

export const getSQLiteEventStore = (
options: SQLiteEventStoreOptions,
): SQLiteEventStore => {
let schemaMigrated = false;
let autoGenerateSchema = false;
let db: SQLiteConnection | null;
const databaseLocation = options.databaseLocation ?? null;
let database: SQLiteConnection | null;
const fileName = options.fileName ?? InMemorySQLiteDatabase;

const isInMemory: boolean = databaseLocation === ':memory:';
const isInMemory: boolean = fileName === InMemorySQLiteDatabase;

const createConnection = () => {
if (db != null) {
return db;
if (database != null) {
return database;
}

return sqliteConnection({
location: databaseLocation,
fileName,
});
};

const closeConnection = () => {
if (isInMemory) {
return;
}
if (db != null) {
db.close();
db = null;
if (database != null) {
database.close();
database = null;
}
};

const withConnection = async <Result>(
handler: (db: SQLiteConnection) => Promise<Result>,
): Promise<Result> => {
if (database == null) {
database = createConnection();
}

try {
await ensureSchemaExists(database);
return await handler(database);
} finally {
closeConnection();
}
};

Expand All @@ -85,13 +100,13 @@ export const getSQLiteEventStore = (
options.schema?.autoMigration !== 'None';
}

const ensureSchemaExists = async (): Promise<void> => {
const ensureSchemaExists = async (
connection: SQLiteConnection,
): Promise<void> => {
if (!autoGenerateSchema) return Promise.resolve();
if (db == null) {
throw new Error('Database connection does not exist');
}

if (!schemaMigrated) {
await createEventStoreSchema(db);
await createEventStoreSchema(connection);
schemaMigrated = true;
}

Expand All @@ -117,19 +132,13 @@ export const getSQLiteEventStore = (
throw new Error('Stream name is not string');
}

if (db == null) {
db = createConnection();
}

let result;
try {
result = await readStream<EventType>(db, streamName, options.read);
} catch (err: Error) {
closeConnection();
throw err;
if (database == null) {
database = createConnection();
}

closeConnection();
const result = await withConnection((db) =>
readStream<EventType>(db, streamName, options.read),
);

const currentStreamVersion = result.currentStreamVersion;

Expand Down Expand Up @@ -157,32 +166,15 @@ export const getSQLiteEventStore = (
options?: ReadStreamOptions<BigIntStreamPosition>,
): Promise<
ReadStreamResult<EventType, ReadEventMetadataWithGlobalPosition>
> => {
if (db == null) {
db = createConnection();
}

let stream;
try {
await ensureSchemaExists();
stream = await readStream<EventType>(db, streamName, options);
} catch (err: Error) {
closeConnection();
throw err;
}

closeConnection();

return stream;
},
> => withConnection((db) => readStream<EventType>(db, streamName, options)),

appendToStream: async <EventType extends Event>(
streamName: string,
events: EventType[],
options?: AppendToStreamOptions,
): Promise<AppendToStreamResult> => {
if (db == null) {
db = createConnection();
): Promise<AppendToStreamResultWithGlobalPosition> => {
if (database == null) {
database = createConnection();
}

// TODO: This has to be smarter when we introduce urn-based resolution
Expand All @@ -191,24 +183,9 @@ export const getSQLiteEventStore = (
const streamType =
firstPart && rest.length > 0 ? firstPart : 'emt:unknown';

let appendResult;

try {
await ensureSchemaExists();

appendResult = await appendToStream(
db,
streamName,
streamType,
events,
options,
);
} catch (err: Error) {
closeConnection();
throw err;
}

closeConnection();
const appendResult = await withConnection((db) =>
appendToStream(db, streamName, streamType, events, options),
);

if (!appendResult.success)
throw new ExpectedVersionConflictError<bigint>(
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { after, before, describe, it } from 'node:test';
import { v4 as uuid } from 'uuid';
import { createEventStoreSchema } from '.';
import {
InMemorySQLiteDatabase,
sqliteConnection,
type SQLiteConnection,
} from '../../sqliteConnection';
Expand All @@ -27,17 +28,22 @@ export type ShoppingCart = {

export type ProductItemAdded = Event<
'ProductItemAdded',
{ productItem: PricedProductItem }
{ productItem: PricedProductItem },
{ meta: string }
>;
export type DiscountApplied = Event<
'DiscountApplied',
{ percent: number },
{ meta: string }
>;
export type DiscountApplied = Event<'DiscountApplied', { percent: number }>;

export type ShoppingCartEvent = ProductItemAdded | DiscountApplied;

void describe('appendEvent', () => {
let db: SQLiteConnection;

before(async () => {
db = sqliteConnection({ location: ':memory:' });
db = sqliteConnection({ fileName: InMemorySQLiteDatabase });
await createEventStoreSchema(db);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import assert from 'assert';
import { after, before, describe, it } from 'node:test';
import {
InMemorySQLiteDatabase,
sqliteConnection,
type SQLiteConnection,
} from '../../sqliteConnection';
Expand All @@ -25,7 +26,7 @@ void describe('createEventStoreSchema', () => {
let db: SQLiteConnection;

before(async () => {
db = sqliteConnection({ location: ':memory:' });
db = sqliteConnection({ fileName: InMemorySQLiteDatabase });

await createEventStoreSchema(db);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { after, before, describe, it } from 'node:test';
import { v4 as uuid } from 'uuid';
import { createEventStoreSchema } from '.';
import {
InMemorySQLiteDatabase,
sqliteConnection,
type SQLiteConnection,
} from '../../sqliteConnection';
Expand All @@ -28,17 +29,22 @@ export type ShoppingCart = {

export type ProductItemAdded = Event<
'ProductItemAdded',
{ productItem: PricedProductItem }
{ productItem: PricedProductItem },
{ meta: string }
>;
export type DiscountApplied = Event<
'DiscountApplied',
{ percent: number },
{ meta: string }
>;
export type DiscountApplied = Event<'DiscountApplied', { percent: number }>;

export type ShoppingCartEvent = ProductItemAdded | DiscountApplied;

void describe('appendEvent', () => {
let db: SQLiteConnection;

before(async () => {
db = sqliteConnection({ location: ':memory:' });
db = sqliteConnection({ fileName: InMemorySQLiteDatabase });
await createEventStoreSchema(db);
});

Expand Down
Loading

0 comments on commit 50da562

Please sign in to comment.