Skip to content

Commit

Permalink
Add testing (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
MeshanKhosla authored Oct 31, 2023
1 parent 0fc9c02 commit 2a3d9b8
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 39 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"files": ["dist"],
"scripts": {
"build": "tsup",
"fmt": "pnpm biome format . --write && pnpm biome check . --apply-unsafe"
"test": "bun test src --coverage",
"fmt": "bunx @biomejs/biome check --apply ."
},
"devDependencies": {
"@biomejs/biome": "1.3.1",
Expand Down
9 changes: 1 addition & 8 deletions src/lock-manager.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import { randomUUID } from "crypto";
import { Redis } from "@upstash/redis";
import { Lock } from "./lock";
import { LockAcquireConfig } from "./types";

type LockManagerConfig = {
/**
* Upstash Redis client instance used for locking operations.
*/
redis: Redis;
};
import type { LockAcquireConfig, LockManagerConfig } from "./types";

export class LockManager {
private readonly redis: Redis;
Expand Down
96 changes: 96 additions & 0 deletions src/lock.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { expect, test } from "bun:test";
import { Redis } from "@upstash/redis";
import { LockManager } from "./lock-manager";

function getUniqueLockId() {
return `lock-test-${Math.random().toString(36).substr(2, 9)}`;
}

test("lock created, extended, and released with defaults", async () => {
const lm = new LockManager({
redis: Redis.fromEnv(),
});
const id = getUniqueLockId();
const lock = await lm.acquire({ id });

expect(lock.id).toBe(id);
expect(lock.status).toBe("ACQUIRED");
const extended = await lock.extend(10000);
expect(extended).toBe(true);
const released = await lock.release();
expect(released).toBe(true);
expect(lock.status).toBe("RELEASED");
});

test("lock created, extended, and released with values", async () => {
const lm = new LockManager({
redis: Redis.fromEnv(),
});

const id = getUniqueLockId();
const lock = await lm.acquire({
id,
lease: 5000,
retry: {
attempts: 1,
delay: 100,
},
});

expect(lock.id).toBe(id);
expect(lock.status).toBe("ACQUIRED");
const extended = await lock.extend(10000);
expect(extended).toBe(true);
const released = await lock.release();
expect(released).toBe(true);
expect(lock.status).toBe("RELEASED");
});

test("lock acquisition fails", async () => {
const lm = new LockManager({
redis: Redis.fromEnv(),
});

const id = getUniqueLockId();
const lockSuccess = await lm.acquire({
id,
lease: 5000,
retry: {
attempts: 1,
delay: 100,
},
});

expect(lockSuccess.status).toBe("ACQUIRED");

// Since the lock was already acquired, this should fail
const lockFail = await lm.acquire({
id,
retry: {
attempts: 1,
delay: 100,
},
});

expect(lockFail.status).toBe("FAILED");
});

test("lock lease times out", async () => {
const lm = new LockManager({
redis: Redis.fromEnv(),
});

const lock = await lm.acquire({
id: "lock-test-3",
lease: 100,
retry: {
attempts: 1,
delay: 100,
},
});

// Wait for the lock to expire
setTimeout(async () => {
expect(lock.status).toBe("RELEASED");
}, 200);
});
35 changes: 5 additions & 30 deletions src/lock.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,4 @@
import { Redis } from "@upstash/redis";
import { LockStatus } from "./types";

type LockConfig = {
/**
* Upstash Redis client instance for locking operations.
*/
redis: Redis;

/**
* Unique identifier associated with the lock.
*/
id: string;

/**
* Current status of the lock (e.g., ACQUIRED, RELEASED).
*/
status: LockStatus;

/**
* Duration (in ms) for which the lock should be held.
*/
lease: number;

/**
* A unique value assigned when the lock is acquired.
* It's set to null if the lock isn't successfully acquired.
*/
UUID: string | null;
};
import type { LockConfig, LockStatus } from "./types";

export class Lock {
private readonly config: LockConfig;
Expand Down Expand Up @@ -84,6 +55,10 @@ export class Lock {
[this.config.id],
[this.config.UUID, extendBy],
);

if (extended === 1) {
this.config.lease += amt;
}
return extended === 1;
}

Expand Down
37 changes: 37 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
import type { Redis } from "@upstash/redis";

export type LockManagerConfig = {
/**
* Upstash Redis client instance used for locking operations.
*/
redis: Redis;
};

export type RetryConfig = {
/**
* The number of times to retry acquiring the lock before giving up.
Expand Down Expand Up @@ -30,4 +39,32 @@ export type LockAcquireConfig = {
retry?: RetryConfig;
};

export type LockConfig = {
/**
* Upstash Redis client instance for locking operations.
*/
redis: Redis;

/**
* Unique identifier associated with the lock.
*/
id: string;

/**
* Current status of the lock (e.g., ACQUIRED, RELEASED).
*/
status: LockStatus;

/**
* Duration (in ms) for which the lock should be held.
*/
lease: number;

/**
* A unique value assigned when the lock is acquired.
* It's set to null if the lock isn't successfully acquired.
*/
UUID: string | null;
};

export type LockStatus = "ACQUIRED" | "RELEASED" | "FAILED";

0 comments on commit 2a3d9b8

Please sign in to comment.