Skip to content

Commit

Permalink
More test coverage and fixes from testing.
Browse files Browse the repository at this point in the history
  • Loading branch information
snoopdave committed Sep 1, 2024
1 parent 1435852 commit 7666477
Show file tree
Hide file tree
Showing 13 changed files with 127 additions and 110 deletions.
2 changes: 1 addition & 1 deletion server/src/apikeys/apikey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class ApiKey extends Model {
allowNull: false
},
created: {
type: DataTypes.STRING,
type: DataTypes.DATE,
allowNull: false
}
}, {
Expand Down
18 changes: 16 additions & 2 deletions server/src/apikeys/apikeystore.it.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import DBConnection from '../utils/dbconnection.js';
import {randomString} from "../utils/utils.js";
import {ApiKeyStore} from "./apikeystore.js";
import ApiKeyStore from "./apikeystore.js";


async function createApiKeyStore(): Promise<{ apiKeyStore: ApiKeyStore, conn: DBConnection }> {
Expand Down Expand Up @@ -79,6 +79,20 @@ describe('Test ApiKeyStore', () => {
await conn.destroy();
}
});
});

test('It can delete an API key', async () => {
const { apiKeyStore, conn } = await createApiKeyStore();
try {
const userId = 'dummy id string';
const key = await apiKeyStore.issue(userId);
expect(await apiKeyStore.verify(userId, key)).toBe(true);

await apiKeyStore.delete(userId);
expect(await apiKeyStore.verify(userId, key)).toBe(false);
} finally {
await conn.destroy();
}
});
})

export {}
42 changes: 7 additions & 35 deletions server/src/apikeys/apikeystore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@
*/

import {v4 as uuid} from 'uuid';
import {DataSource, DataSourceConfig} from "apollo-datasource";
import {DataSource} from "apollo-datasource";
import DBConnection from "../utils/dbconnection.js";
import sequelize from 'sequelize';
import {randomString} from "../utils/utils.js";
import {Blog} from "../blogs/blog.js";
import {ApiKey} from "./apikey";
import {DataSourceConfig} from "apollo-datasource/src";

const { DataTypes } = sequelize; // sequelize is a CommonJS module


export class ApiKeyStore implements DataSource<ApiKey> {
export default class ApiKeyStore implements DataSource<ApiKey> {
db: sequelize.Sequelize;

constructor(conn: DBConnection) {
Expand All @@ -25,43 +23,17 @@ export class ApiKeyStore implements DataSource<ApiKey> {
}

async init() {
ApiKey.init({
id: {
type: DataTypes.STRING,
allowNull: false,
primaryKey: true
},
apiKey: { // TODO: encryption
type: DataTypes.STRING,
allowNull: false,
field: 'api_key',
},
userId: {
type: DataTypes.STRING,
allowNull: false,
},
created: {
type: DataTypes.DATE,
allowNull: false
},
}, {
sequelize: this.db,
modelName: 'apiKey',
createdAt: 'created',
updatedAt: 'updated'
});
await ApiKey.sync();
await ApiKey.initialize(this.db);
}

async issue(userId: string): Promise<string> {
// user can have one and only one key
const existingKey: ApiKey | null = await ApiKey.findOne({where: {userId}});
const now = new Date();
const newKey = randomString(10);
if (existingKey) { // issue and replace existing apiKey
const [number] = await ApiKey.update({
apiKey: newKey,
created: now
created: new Date().getTime()
},
{where: {id: existingKey.id}});
if (number === 0) {
Expand All @@ -74,15 +46,15 @@ export class ApiKeyStore implements DataSource<ApiKey> {
id: `${uuid()}-apikey`,
userId,
apiKey: newKey,
created: now,
created: new Date().getTime(),
});
await createdApiKey.save();
}
return newKey;
}

async delete(userId: string) {
const number = await Blog.destroy({where: {userId}});
const number = await ApiKey.destroy({where: {userId}});
if (number === 0) {
throw Error('API key not found');
} else if (number > 1) {
Expand Down
18 changes: 18 additions & 0 deletions server/src/blogs/blogstore.it.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,24 @@ describe('Test BlogStore', () => {
await conn.destroy();
}
});

test('It will not allow a user to create more than one blog', async () => {
let dbslug = randomString(5);
let conn = new DBConnection(`./db-test-${dbslug}.db`);
let blogStore = new BlogStore(conn);
await blogStore.init();
const userStore = new UserStore(conn);
await userStore.init();
try {
let slug = randomString(5);
const user: User = await userStore.create(
`test-user-${slug}`, 'test-user-${slug}@example.com', 'dummy.png')
await blogStore.create(user.id, `myblog-${slug}`, `My Blog ${slug}`);
await expect(blogStore.create(user.id, `myblog2-${slug}`, `My Blog 2 ${slug}`)).rejects.toThrow();
} finally {
await conn.destroy();
}
});
});

export {}
1 change: 0 additions & 1 deletion server/src/blogs/blogstore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {DataSourceConfig} from 'apollo-datasource/src';
import {FindAllResult} from '../pagination.js';
import {Blog} from "./blog.js";

const { DataTypes} = sequelize; // sequelize is a CommonJS module

export default class BlogStore implements DataSource<Blog> {
db: sequelize.Sequelize;
Expand Down
2 changes: 1 addition & 1 deletion server/src/blogs/blogstoreauth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import DBConnection from "../utils/dbconnection";
import { User } from "../users/user";
import { Blog } from "./blog";
import { Entry } from "../entries/entry";
import { ApiKeyStore } from "../apikeys/apikeystore";
import ApiKeyStore from "../apikeys/apikeystore";
import { FindAllResult } from "../pagination";

describe('BlogService Authentication Tests', () => {
Expand Down
3 changes: 1 addition & 2 deletions server/src/blogservice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import {Node} from './types/node.js';
import {Cursor, resolveCollection, ResponseConnection, ResponseEdge} from './pagination.js';
import {DEBUG, log} from "./utils/utils.js";
import DBConnection from "./utils/dbconnection.js";
import {ApiKeyStore} from "./apikeys/apikeystore.js";
import BlogStore from "./blogs/blogstore.js";
import ApiKeyStore from "./apikeys/apikeystore.js";import BlogStore from "./blogs/blogstore.js";
import {EntryStore} from "./entries/entrystore.js";
import {UserStore} from "./users/userstore.js";
import {GraphQLError} from "graphql/error/GraphQLError.js"; // this is an annoying one
Expand Down
39 changes: 1 addition & 38 deletions server/src/entries/entrystore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,44 +24,7 @@ export class EntryStore implements DataSource<Entry> {
initialize?(config: DataSourceConfig<Entry>): void | Promise<void> {}

async init() {
Entry.init({
id: {
type: DataTypes.STRING,
allowNull: false,
primaryKey: true
},
blogId: {
type: DataTypes.STRING,
allowNull: false,
field: 'blog_id'
},
title: {
type: DataTypes.STRING,
allowNull: false
},
content: {
type: DataTypes.STRING,
allowNull: false
},
created: {
type: DataTypes.DATE,
allowNull: false
},
updated: {
type: DataTypes.DATE,
allowNull: false
},
published: {
type: DataTypes.DATE,
allowNull: true
},
}, {
sequelize: this.db,
modelName: 'entry',
createdAt: 'created',
updatedAt: 'updated'
});
await Entry.sync();
await Entry.initialize(this.db);
}

async create(blogId: string, title: string, content: string) : Promise<Entry> {
Expand Down
2 changes: 1 addition & 1 deletion server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import BlogQL from './blogql.js';
import {DEBUG, INFO, log} from './utils/utils.js';
import {readFileSync} from 'fs';
import {config} from './utils/config.js';
import {ApiKeyStore} from "./apikeys/apikeystore.js";
import ApiKeyStore from "./apikeys/apikeystore.js";
import {BlogService, BlogServiceSequelizeImpl} from "./blogservice.js";
import {UserStore} from "./users/userstore.js";
import {expressMiddleware} from "@apollo/server/express4";
Expand Down
26 changes: 6 additions & 20 deletions server/src/pagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,26 +66,12 @@ export class FindAllArgs {
after?: string;
last?: number;
before?: string;
constructor(first?: number, after?: string, last?: number, before?: string) {
this.first = first;
this.after = after;
this.last = last;
this.before = before;
}
}

export class FindAllResult<T> {
rows: T[];
count: number;
totalCount: number;
constructor(rows: T[], count: number, totalCount: number) {
this.rows = rows;
this.count = count;
if (!totalCount) {
throw new Error('totalCount must be provided');
}
this.totalCount = totalCount;
}
rows?: T[];
count?: number;
totalCount?: number;
}

function log(msg: string) {
Expand Down Expand Up @@ -117,10 +103,10 @@ export async function resolveCollection<T>(
// ask for one more than we need to see if there is a next page
const result: FindAllResult<T> = await fetchData(new Cursor(limit + 1, offset));

const hasNextPage = result.rows.length > limit;
const hasNextPage = result.rows!.length > limit;
const hasPreviousPage = offset > 0;

const edges: ResponseEdge<T>[] = result.rows.slice(0, limit).map((row, index) => {
const edges: ResponseEdge<T>[] = result.rows!.slice(0, limit).map((row, index) => {
const rowCursor: Cursor = new Cursor(limit, offset + index);
return new ResponseEdge<T>(row, rowCursor.encode());
});
Expand All @@ -133,7 +119,7 @@ export async function resolveCollection<T>(
hasPreviousPage,
startCursor,
endCursor,
result.count
result.count!
));
} catch (error: unknown) {
if (error instanceof Error) {
Expand Down
10 changes: 5 additions & 5 deletions server/src/tests/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,16 @@ describe('Test the GraphQL API integration', () => {
test('It can get blog for a user', async () => {
const {blogService, server, conn, authUsers} = await initDataStorage();
const slug = randomString(5);
const blog = await createBlog(server, `My Blog ${slug}`, `myblog${slug}`, { blogService, user: authUsers[0] });
const blog = await createBlog(server, `myblog${slug}`, `My Blog ${slug}`, { blogService, user: authUsers[0] });

expect(blog.body).toEqual({
kind: 'single',
singleResult: {
data: {
createBlog: {
id: expect.any(String),
handle: `My Blog ${slug}`,
name: `myblog${slug}`,
name: `My Blog ${slug}`,
handle: `myblog${slug}`,
created: expect.any(Date),
updated: expect.any(Date),
userId: authUsers[0].id,
Expand All @@ -121,8 +121,8 @@ describe('Test the GraphQL API integration', () => {
data: {
blogForUser: {
id: blog.body.kind === 'single' ? (blog.body.singleResult?.data?.createBlog as {id: string}).id : undefined,
handle: `My Blog ${slug}`,
name: `myblog${slug}`,
name: `My Blog ${slug}`,
handle: `myblog${slug}`,
created: expect.any(Date),
updated: expect.any(Date)
}
Expand Down
67 changes: 67 additions & 0 deletions server/src/users/userstore.it.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,73 @@ describe('Test UserStore', () => {
await conn.destroy();
}
});

it("Can update of user picture and username", async () => {
let slug = randomString(5);
let conn = new DBConnection(`./db-test-${slug}.db`);
const us = new UserStore(conn);
await us.init();

try {
let user = await us.upsert('Test User', '[email protected]', 'https://example.com/test.png');
const updatedUser = await us.update(user.id, 'Updated User', 'https://example.com/updated.png');
if (updatedUser) {
expect(updatedUser).toEqual({
id: expect.any(String),
username: 'Updated User',
email: '[email protected]',
picture: 'https://example.com/updated.png',
created: expect.any(Date),
updated: expect.any(Date)
});
} else {
throw new Error('User update failed');
}
} finally {
await conn.destroy();
}
});

it("It throws error if attempt to update user that does not exist", async () => {
let slug = randomString(5);
let conn = new DBConnection(`./db-test-${slug}.db`);
const us = new UserStore(conn);
await us.init();

try {
await expect(us.update('non-existent-id', 'Non Existent User', 'https://example.com/nonexistent.png')).rejects.toThrow('User not found');
} finally {
await conn.destroy();
}
});

it("It throws error on attempt to update user that does not exist", async () => {
let slug = randomString(5);
let conn = new DBConnection(`./db-test-${slug}.db`);
const us = new UserStore(conn);
await us.init();

try {
await expect(us.update('non-existent-id', 'Non Existent User', 'https://example.com/nonexistent.png')).rejects.toThrow('User not found');
} finally {
await conn.destroy();
}
});

it("It throws error on attempt to delete user that does not exist", async () => {
let slug = randomString(5);
let conn = new DBConnection(`./db-test-${slug}.db`);
const us = new UserStore(conn);
await us.init();

try {
await expect(us.delete('non-existent-id')).rejects.toThrow('User not found');
} finally {
await conn.destroy();
}
});


});

export {}
Loading

0 comments on commit 7666477

Please sign in to comment.