Skip to content
This repository has been archived by the owner on Dec 4, 2023. It is now read-only.

Commit

Permalink
Merge pull request #83 from Asjas/fix-test-workflow
Browse files Browse the repository at this point in the history
add back test and coverage scripts to main workflow
  • Loading branch information
Asjas authored Mar 28, 2022
2 parents 517722f + f65f487 commit 9086ba7
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 98 deletions.
13 changes: 12 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:

strategy:
matrix:
node: ["12", "14", "16"]
node: ["14", "16"]
os: [ubuntu-latest, windows-latest, macOS-latest]

steps:
Expand All @@ -29,3 +29,14 @@ jobs:

- name: Run validation npm script
run: npm run validate

- name: Run the tests
run: npm test

- name: Generate coverage report
run: npm run coverage

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
with:
token: ${{ secrets.CODECOV_TOKEN }}
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ Uses [async-cache-dedupe](https://github.com/mcollina/async-cache-dedupe).

The latest versions of the following Node.js versions are tested and supported.

- 12
- 14
- 16

Expand Down
20 changes: 17 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,23 @@ export const createPrismaRedisCache = ({
}
});

// Define a cache function for any Prisma model if `models` was not defined
// Only define the cache function for a model if it doesn't exist yet and hasn't been excluded
if (!models?.length && !cache[params.model] && !defaultExcludeCacheModels?.includes(params.model)) {
// For each defined model in `models` we check if they defined any caching methods to be excluded
const excludedCacheMethodsInModels = models?.find(({ model, excludeCacheMethods }) => {
if (model === params.model && excludeCacheMethods?.length) {
return true;
}

return false;
});

// Do not define a cache function for any Prisma model if it already exists
// Do not define the cache function for a model if it was excluded in `defaultExcludeCacheModels`
// Do not define a cache function if the Prisma method was exluded in `models`
if (
!cache[params.model] &&
!defaultExcludeCacheModels?.includes(params.model) &&
!excludedCacheMethodsInModels?.excludeCacheMethods?.includes(params.action as PrismaQueryAction)
) {
cache.define(
params.model,
{
Expand Down
177 changes: 84 additions & 93 deletions test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import MockRedis from "ioredis-mock";
import type Redis from "ioredis";
import { test, expect, afterEach, assert } from "vitest";
import { createPrismaRedisCache } from "../src";

import type Redis from "ioredis";
import { createPrismaRedisCache } from "../src";

// Create the mock Redis instance we need
// Do some funky shit to get TypeScript to be happy 😫
Expand All @@ -13,16 +13,19 @@ afterEach(async () => {
await redis.flushall();
});

test("should cache a single Prisma model", async () => {
// Do some setup stuff
const dbValue = { key: "test result" };
const model = "User";
const action = "findUnique";
const args = { where: { foo: "bar" } };
const defaultCacheTime = 2000; // 2 seconds
const cacheKey = `User~{"params":{"action":"findUnique","args":{"where":{"foo":"bar"}},"dataPath":[],"model":"User","runInTransaction":false}}`;
const next = () => Promise.resolve(dbValue);
// Do some setup stuff
const dbValue = { key: "test result" };
const model1 = "User";
const model2 = "Post";
const action1 = "findUnique";
const action2 = "findFirst";
const args = { where: { foo: "bar" } };
const defaultCacheTime = 2000; // 2 seconds
const cacheKey1 = `${model1}~{"params":{"action":"${action1}","args":{"where":{"foo":"bar"}},"dataPath":[],"model":"${model1}","runInTransaction":false}}`;
const cacheKey2 = `${model2}~{"params":{"action":"${action2}","args":{"where":{"foo":"bar"}},"dataPath":[],"model":"${model2}","runInTransaction":false}}`;
const next = () => Promise.resolve(dbValue);

test("should cache a single Prisma model", async () => {
const middleware = createPrismaRedisCache({
defaultCacheTime,
storage: { type: "redis", options: { client: redis } },
Expand All @@ -32,29 +35,20 @@ test("should cache a single Prisma model", async () => {
await middleware(
{
args,
action,
model,
action: action1,
model: model1,
dataPath: [],
runInTransaction: false,
},
next,
);

// Test if the data exists in the cache
expect(JSON.parse((await redis.get(cacheKey)) as string)).toMatchObject(dbValue);
expect(JSON.parse((await redis.get(cacheKey1)) as string)).toMatchObject(dbValue);
});

test("should cache multiple Prisma models in cache", async () => {
// Do some setup stuff
const dbValue = { key: "test result" };
const model1 = "User";
const model2 = "Post";
const action = "findUnique";
const args = { where: { foo: "bar" } };
const cacheTime = 2000; // 2 seconds
const cacheKey1 = `User~{"params":{"action":"findUnique","args":{"where":{"foo":"bar"}},"dataPath":[],"model":"User","runInTransaction":false}}`;
const cacheKey2 = `Post~{"params":{"action":"findUnique","args":{"where":{"foo":"bar"}},"dataPath":[],"model":"Post","runInTransaction":false}}`;
const next = () => Promise.resolve(dbValue);

const middleware = createPrismaRedisCache({
models: [
Expand All @@ -68,7 +62,7 @@ test("should cache multiple Prisma models in cache", async () => {
await middleware(
{
args,
action,
action: action1,
model: model1,
dataPath: [],
runInTransaction: false,
Expand All @@ -80,7 +74,7 @@ test("should cache multiple Prisma models in cache", async () => {
await middleware(
{
args,
action,
action: action2,
model: model2,
dataPath: [],
runInTransaction: false,
Expand All @@ -94,18 +88,11 @@ test("should cache multiple Prisma models in cache", async () => {
});

test("should use custom cacheKey when caching a Prisma model", async () => {
// Do some setup stuff
const dbValue = { key: "test result" };
const model = "Post";
const action = "findFirst";
const args = { where: { foo: "bar" } };
const defaultCacheTime = 2000; // 2 seconds
const customCacheKey = "Article";
const cacheKey = `${customCacheKey}~{"params":{"action":"findFirst","args":{"where":{"foo":"bar"}},"dataPath":[],"model":${customCacheKey},"runInTransaction":false}}`;
const next = () => Promise.resolve(dbValue);
const cacheKey = `${customCacheKey}~{"params":{"action":"${action1}","args":{"where":{"foo":"bar"}},"dataPath":[],"model":${customCacheKey},"runInTransaction":false}}`;

const middleware = createPrismaRedisCache({
models: [{ model, cacheKey: customCacheKey }],
models: [{ model: model1, cacheKey: customCacheKey }],
storage: { type: "redis", options: { client: redis } },
defaultCacheTime,
});
Expand All @@ -114,8 +101,8 @@ test("should use custom cacheKey when caching a Prisma model", async () => {
await middleware(
{
args,
action,
model,
action: action1,
model: model1,
dataPath: [],
runInTransaction: false,
},
Expand All @@ -127,17 +114,8 @@ test("should use custom cacheKey when caching a Prisma model", async () => {
});

test("should exclude Prisma action from being cached with excludeCacheMethods", async () => {
// Do some setup stuff
const dbValue = { key: "test result" };
const model = "User";
const action = "findFirst";
const args = { where: { foo: "bar" } };
const defaultCacheTime = 2000; // 2 seconds
const cacheKey = `User~{"params":{"action":"findFirst","args":{"where":{"foo":"bar"}},"dataPath":[],"model":"User","runInTransaction":false}}`;
const next = () => Promise.resolve(dbValue);

const middleware = createPrismaRedisCache({
models: [{ model, excludeCacheMethods: [action] }],
models: [{ model: model1, excludeCacheMethods: [action1] }],
storage: { type: "redis", options: { client: redis } },
defaultCacheTime,
});
Expand All @@ -146,95 +124,107 @@ test("should exclude Prisma action from being cached with excludeCacheMethods",
await middleware(
{
args,
action,
model,
action: action1,
model: model1,
dataPath: [],
runInTransaction: false,
},
next,
);

// Run a "fake" Post Prisma query
await middleware(
{
args,
action: action2,
model: model2,
dataPath: [],
runInTransaction: false,
},
next,
);

// Test if the query was skipped and does not exist in cache
assert.equal(JSON.parse((await redis.get(cacheKey)) as string), null);
assert.equal(JSON.parse((await redis.get(cacheKey1)) as string), null);
expect(JSON.parse((await redis.get(cacheKey2)) as string)).toMatchObject(dbValue);
});

test("should exclude Prisma method from beign cached with defaultExcludeCacheMethods", async () => {
// Do some setup stuff
const dbValue = { key: "test result" };
const model = "User";
const action = "findFirst";
const args = { where: { foo: "bar" } };
const defaultCacheTime = 2000; // 2 seconds
const cacheKey = `User~{"params":{"action":"findFirst","args":{"where":{"foo":"bar"}},"dataPath":[],"model":"User","runInTransaction":false}}`;
const next = () => Promise.resolve(dbValue);

test("should exclude a Prisma method from being cached with defaultExcludeCacheMethods", async () => {
const middleware = createPrismaRedisCache({
models: [{ model }],
models: [{ model: model1 }],
storage: { type: "redis", options: { client: redis } },
defaultCacheTime,
defaultExcludeCacheMethods: [action],
defaultExcludeCacheMethods: [action1],
});

// Run a "fake" User Prisma query
await middleware(
{
args,
action,
model,
action: action1,
model: model1,
dataPath: [],
runInTransaction: false,
},
next,
);

// Run a "fake" Post Prisma query
await middleware(
{
args,
action: action2,
model: model2,
dataPath: [],
runInTransaction: false,
},
next,
);

// Test if the query was skipped and does not exist in cache
assert.equal(JSON.parse((await redis.get(cacheKey)) as string), null);
assert.equal(JSON.parse((await redis.get(cacheKey1)) as string), null);
// Test that (non-excluded) queries are still cached
expect(JSON.parse((await redis.get(cacheKey2)) as string)).toMatchObject(dbValue);
});

test("should exclude Prisma model from being cached with defaultExcludeCacheModels", async () => {
// Do some setup stuff
const dbValue = { key: "test result" };
const model = "User";
const action = "findFirst";
const args = { where: { foo: "bar" } };
const defaultCacheTime = 2000; // 2 seconds
const cacheKey = `User~{"params":{"action":"findFirst","args":{"where":{"foo":"bar"}},"dataPath":[],"model":"User","runInTransaction":false}}`;
const next = () => Promise.resolve(dbValue);

test("should exclude a Prisma model from being cached with defaultExcludeCacheModels", async () => {
const middleware = createPrismaRedisCache({
storage: { type: "redis", options: { client: redis } },
defaultCacheTime,
defaultExcludeCacheModels: [model],
defaultExcludeCacheModels: [model1],
});

// Run a "fake" User Prisma query
await middleware(
{
args,
action,
model,
action: action1,
model: model1,
dataPath: [],
runInTransaction: false,
},
next,
);

// Test if the query was skipped and does not exist in cache
assert.equal(JSON.parse((await redis.get(cacheKey)) as string), null);
// Run a "fake" Post Prisma query
await middleware(
{
args,
action: action2,
model: model2,
dataPath: [],
runInTransaction: false,
},
next,
);

// Test if the Model was skipped and does not exist in cache
assert.equal(JSON.parse((await redis.get(cacheKey1)) as string), null);
// Make sure that other (non-excluded) models are still cached
expect(JSON.parse((await redis.get(cacheKey2)) as string)).toMatchObject(dbValue);
});

test("should invalidate a single Prisma model cache after data mutation", async () => {
// Do some setup stuff
const dbValue = { key: "test result" };
const model1 = "User";
const model2 = "Post";
const action = "findUnique";
const args = { where: { foo: "bar" } };
const defaultCacheTime = 2000; // 2 seconds
const cacheKey1 = `User~{"params":{"action":"findUnique","args":{"where":{"foo":"bar"}},"dataPath":[],"model":"User","runInTransaction":false}}`;
const cacheKey2 = `Post~{"params":{"action":"findUnique","args":{"where":{"foo":"bar"}},"dataPath":[],"model":"Post","runInTransaction":false}}`;
const next = () => Promise.resolve(dbValue);

const middleware = createPrismaRedisCache({
storage: { type: "redis", options: { client: redis, invalidation: true } },
defaultCacheTime,
Expand All @@ -244,7 +234,7 @@ test("should invalidate a single Prisma model cache after data mutation", async
await middleware(
{
args,
action,
action: action1,
model: model1,
dataPath: [],
runInTransaction: false,
Expand All @@ -256,15 +246,15 @@ test("should invalidate a single Prisma model cache after data mutation", async
await middleware(
{
args,
action,
action: action2,
model: model2,
dataPath: [],
runInTransaction: false,
},
next,
);

// Test if data exists in the Redis cache
// Test if data exists in the cache
expect(JSON.parse((await redis.get(cacheKey1)) as string)).toMatchObject(dbValue);
expect(JSON.parse((await redis.get(cacheKey2)) as string)).toMatchObject(dbValue);

Expand All @@ -282,5 +272,6 @@ test("should invalidate a single Prisma model cache after data mutation", async

// Test if the cache was invalidated and cleared properly
assert.equal(JSON.parse((await redis.get(cacheKey1)) as string), null);
// Test that we keep other cached queries that shouldn't be cleared
expect(JSON.parse((await redis.get(cacheKey2)) as string)).toMatchObject(dbValue);
});

0 comments on commit 9086ba7

Please sign in to comment.