Skip to content

Commit

Permalink
Added dummy implementation of Mongo shim
Browse files Browse the repository at this point in the history
  • Loading branch information
oskardudycz committed Jul 5, 2024
1 parent d3ed45a commit a8b9edd
Show file tree
Hide file tree
Showing 14 changed files with 1,844 additions and 67 deletions.
977 changes: 955 additions & 22 deletions src/package-lock.json

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
],
"devDependencies": {
"@faker-js/faker": "8.4.1",
"@testcontainers/mongodb": "^10.10.1",
"@testcontainers/postgresql": "^10.10.1",
"@types/mongodb": "^4.0.7",
"@types/node": "20.11.30",
"@types/pg": "^8.11.6",
Expand All @@ -85,12 +87,14 @@
"vitepress": "1.0.1"
},
"peerDependencies": {
"close-with-grace": "^1.3.0",
"pg": "^8.12.0",
"pg-format": "^1.0.4",
"close-with-grace": "^1.3.0"
"pg-format": "^1.0.4"
},
"workspaces": [
"packages/pongo"
],
"dependencies": {}
"dependencies": {
"testcontainers": "^10.10.1"
}
}
217 changes: 217 additions & 0 deletions src/packages/pongo/src/e2e/compatibilityTest.e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
import {
MongoDBContainer,
type StartedMongoDBContainer,
} from '@testcontainers/mongodb';
import {
PostgreSqlContainer,
type StartedPostgreSqlContainer,
} from '@testcontainers/postgresql';
import assert from 'assert';
import { Db as MongoDb, MongoClient as OriginalMongoClient } from 'mongodb';
import { after, before, describe, it } from 'node:test';
import MongoClient from '../mongo/mongoClient';
import type { Db } from '../mongo/mongoDb';
import { endAllPools } from '../postgres';

type User = { name: string; age: number };

void describe('MongoDB Compatibility Tests', () => {
let postgres: StartedPostgreSqlContainer;
let postgresConnectionString: string;
let pongoClient: MongoClient;

let mongo: StartedMongoDBContainer;
let mongoConnectionString: string;
let mongoClient: OriginalMongoClient;

let pongoDb: Db;
let mongoDb: MongoDb;

before(async () => {
postgres = await new PostgreSqlContainer().start();
postgresConnectionString = postgres.getConnectionUri();
pongoClient = new MongoClient(postgresConnectionString);
await pongoClient.connect();

mongo = await new MongoDBContainer('mongo:6.0.12').start();
mongoConnectionString = mongo.getConnectionString();
mongoClient = new OriginalMongoClient(mongoConnectionString, {
directConnection: true,
});
await mongoClient.connect();

const dbName = postgres.getDatabase();

pongoDb = pongoClient.db(dbName);
mongoDb = mongoClient.db(dbName);
});

after(async () => {
try {
await endAllPools();
await postgres.stop();
} catch (error) {
console.log(error);
}
try {
await mongoClient.close();
await mongo.stop();
} catch (error) {
console.log(error);
}
});

void describe('Insert Operations', () => {
void it('should insert a document into both PostgreSQL and MongoDB', async () => {
const pongoCollection = pongoDb.collection<User>('testCollection');
const mongoCollection = mongoDb.collection<User>('testCollection');

const doc = { name: 'Alice', age: 25 };

const pongoInsertResult = await pongoCollection.insertOne(doc);
const mongoInsertResult = await mongoCollection.insertOne(doc);

assert(pongoInsertResult.insertedId);
assert(mongoInsertResult.insertedId);

const pongoDoc = await pongoCollection.findOne({
_id: pongoInsertResult.insertedId,
});
const mongoDoc = await mongoCollection.findOne({
_id: mongoInsertResult.insertedId,
});

assert.deepStrictEqual(
{
name: pongoDoc!.name,
age: pongoDoc!.age,
},
{
name: mongoDoc!.name,
age: mongoDoc!.age,
},
);
});
});

void describe('Update Operations', () => {
void it('should update a document in both PostgreSQL and MongoDB', async () => {
const pongoCollection = pongoDb.collection<User>('testCollection');
const mongoCollection = mongoDb.collection<User>('testCollection');
const doc = { name: 'Bob', age: 30 };

const pongoInsertResult = await pongoCollection.insertOne(doc);
const mongoInsertResult = await mongoCollection.insertOne(doc);

const update = { $set: { age: 31 } };

await pongoCollection.updateOne(
{ _id: pongoInsertResult.insertedId },
update,
);
await mongoCollection.updateOne(
{ _id: mongoInsertResult.insertedId },
update,
);

const pongoDoc = await pongoCollection.findOne({
_id: pongoInsertResult.insertedId,
});
const mongoDoc = await mongoCollection.findOne({
_id: mongoInsertResult.insertedId,
});

assert.deepStrictEqual(
{
name: pongoDoc!.name,
age: 31,
},
{
name: mongoDoc!.name,
age: 31,
},
);
});
});

void describe('Delete Operations', () => {
void it('should delete a document from both PostgreSQL and MongoDB', async () => {
const pongoCollection = pongoDb.collection<User>('testCollection');
const mongoCollection = mongoDb.collection<User>('testCollection');
const doc = { name: 'Charlie', age: 35 };

const pongoInsertResult = await pongoCollection.insertOne(doc);
const mongoInsertResult = await mongoCollection.insertOne(doc);

await pongoCollection.deleteOne({ _id: pongoInsertResult.insertedId });
await mongoCollection.deleteOne({ _id: mongoInsertResult.insertedId });

const pongoDoc = await pongoCollection.findOne({
_id: pongoInsertResult.insertedId,
});
const mongoDoc = await mongoCollection.findOne({
_id: mongoInsertResult.insertedId,
});

assert.strictEqual(pongoDoc, null);
assert.strictEqual(mongoDoc, null);
});
});

void describe('Find Operations', () => {
void it('should find documents with a filter in both PostgreSQL and MongoDB', async () => {
const pongoCollection = pongoDb.collection<User>('testCollection');
const mongoCollection = mongoDb.collection<User>('testCollection');
const docs = [
{ name: 'David', age: 40 },
{ name: 'Eve', age: 45 },
{ name: 'Frank', age: 50 },
];

await pongoCollection.insertOne(docs[0]!);
await pongoCollection.insertOne(docs[1]!);
await pongoCollection.insertOne(docs[2]!);

await mongoCollection.insertOne(docs[0]!);
await mongoCollection.insertOne(docs[1]!);
await mongoCollection.insertOne(docs[2]!);

const pongoDocs = await pongoCollection
.find({ age: { $gte: 45 } })
.toArray();
const mongoDocs = await mongoCollection
.find({ age: { $gte: 45 } })
.toArray();

assert.strictEqual(pongoDocs.length, 2);

assert.deepStrictEqual(
pongoDocs.map((d) => ({ name: d.name, age: d.age })),
mongoDocs.map((d) => ({ name: d.name, age: d.age })),
);
});

void it('should find one document with a filter in both PostgreSQL and MongoDB', async () => {
const pongoCollection = pongoDb.collection<User>('testCollection');
const mongoCollection = mongoDb.collection<User>('testCollection');
const doc = { name: 'Grace', age: 55 };

await pongoCollection.insertOne(doc);
await mongoCollection.insertOne(doc);

const pongoDoc = await pongoCollection.findOne({ name: 'Grace' });
const mongoDoc = await mongoCollection.findOne({ name: 'Grace' });

assert.deepStrictEqual(
{
name: pongoDoc!.name,
age: pongoDoc!.age,
},
{
name: mongoDoc!.name,
age: mongoDoc!.age,
},
);
});
});
});
9 changes: 7 additions & 2 deletions src/packages/pongo/src/main/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ import type { PongoClient, PongoDb } from './typing';
export const pongoClient = (connectionString: string): PongoClient => {
const dbClient = getDbClient(connectionString);

return {
connect: () => dbClient.connect(),
const pongoClient: PongoClient = {
connect: async () => {
await dbClient.connect();
return pongoClient;
},
close: () => dbClient.close(),
db: (dbName?: string): PongoDb =>
dbName ? getDbClient(connectionString, dbName) : dbClient,
};

return pongoClient;
};
14 changes: 8 additions & 6 deletions src/packages/pongo/src/main/typing.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export interface PongoClient {
connect(): Promise<void>;
connect(): Promise<this>;

close(): Promise<void>;

Expand All @@ -12,7 +12,7 @@ export interface PongoDb {

export interface PongoCollection<T> {
createCollection(): Promise<void>;
insertOne(document: T): Promise<PongoInsertResult>;
insertOne(document: T): Promise<PongoInsertOneResult>;
updateOne(
filter: PongoFilter<T>,
update: PongoUpdate<T>,
Expand Down Expand Up @@ -44,15 +44,17 @@ export type PongoUpdate<T> = {
$push?: { [P in keyof T]?: T[P] };
};

export interface PongoInsertResult {
export interface PongoInsertOneResult {
insertedId: string | null;
insertedCount: number | null;
acknowledged: boolean;
}

export interface PongoUpdateResult {
modifiedCount: number | null;
acknowledged: boolean;
modifiedCount: number;
}

export interface PongoDeleteResult {
deletedCount: number | null;
acknowledged: boolean;
deletedCount: number;
}
37 changes: 37 additions & 0 deletions src/packages/pongo/src/mongo/findCursor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export class FindCursor<T> {
private findDocumentsPromise: Promise<T[]>;
private documents: T[] | null = null;
private index: number = 0;

constructor(documents: Promise<T[]>) {
this.findDocumentsPromise = documents;
}

async toArray(): Promise<T[]> {
return this.findDocuments();
}

async forEach(callback: (doc: T) => void): Promise<void> {
const docs = await this.findDocuments();

for (const doc of docs) {
callback(doc);
}
return Promise.resolve();
}

hasNext(): boolean {
if (this.documents === null) throw Error('Error while fetching documents');
return this.index < this.documents.length;
}

async next(): Promise<T | null> {
const docs = await this.findDocuments();
return this.hasNext() ? docs[this.index++] ?? null : null;
}

private async findDocuments(): Promise<T[]> {
this.documents = await this.findDocumentsPromise;
return this.documents;
}
}
Empty file.
24 changes: 24 additions & 0 deletions src/packages/pongo/src/mongo/mongoClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// src/MongoClientShim.ts
import { pongoClient, type PongoClient } from '../main';
import { Db } from './mongoDb';

export default class MongoClient {
private pongoClient: PongoClient;

constructor(connectionString: string) {
this.pongoClient = pongoClient(connectionString);
}

async connect() {
await this.pongoClient.connect();
return this;
}

async close() {
await this.pongoClient.close();
}

db(dbName: string): Db {
return new Db(this.pongoClient.db(dbName));
}
}
Loading

0 comments on commit a8b9edd

Please sign in to comment.