modified: src/db/index.ts
deleted: src/db/seed.test.ts deleted: src/db/seed.ts
This commit is contained in:
+1
-3
@@ -7,7 +7,6 @@ import { PickList } from '../models/PickList';
|
||||
import { Product } from '../models/Product';
|
||||
import { ImportExportLog } from '../models/ImportExportLog';
|
||||
import { applyMigrations } from './migrations';
|
||||
import { seedDatabase } from './seed';
|
||||
|
||||
export class StockFillDB extends Dexie {
|
||||
products!: Table<Product>;
|
||||
@@ -254,5 +253,4 @@ export const db = new StockFillDB();
|
||||
|
||||
export const initializeDatabase = async () => {
|
||||
await applyMigrations(db);
|
||||
await seedDatabase(db);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { seedAreas, seedCategories, seedDatabase, seedProducts } from './seed';
|
||||
import { Area } from '../models/Area';
|
||||
import { Category } from '../models/Category';
|
||||
import { DEFAULT_BULK_NAME, DEFAULT_UNIT_TYPE, Product } from '../models/Product';
|
||||
import { StockFillDB } from './index';
|
||||
|
||||
const normalizeName = (name: string) => name.trim().toLowerCase();
|
||||
|
||||
class MockTable<T extends { id: string; name: string }> {
|
||||
constructor(public items: T[] = []) {}
|
||||
|
||||
async count() {
|
||||
return this.items.length;
|
||||
}
|
||||
|
||||
async bulkAdd(records: T[]) {
|
||||
this.items.push(...records);
|
||||
}
|
||||
|
||||
async bulkDelete(ids: string[]) {
|
||||
this.items = this.items.filter((item) => !ids.includes(item.id));
|
||||
}
|
||||
|
||||
async toArray() {
|
||||
return [...this.items];
|
||||
}
|
||||
}
|
||||
|
||||
const buildArea = (overrides: Partial<Area> = {}): Area => ({
|
||||
id: overrides.id ?? uuidv4(),
|
||||
name: overrides.name ?? 'Area',
|
||||
created_at: overrides.created_at ?? Date.now(),
|
||||
updated_at: overrides.updated_at ?? Date.now(),
|
||||
});
|
||||
|
||||
const buildProduct = (overrides: Partial<Product> = {}): Product => ({
|
||||
id: overrides.id ?? uuidv4(),
|
||||
name: overrides.name ?? 'Product',
|
||||
category: overrides.category ?? 'Category',
|
||||
unit_type: overrides.unit_type ?? DEFAULT_UNIT_TYPE,
|
||||
bulk_name: overrides.bulk_name ?? DEFAULT_BULK_NAME,
|
||||
barcode: overrides.barcode,
|
||||
archived: overrides.archived ?? false,
|
||||
created_at: overrides.created_at ?? Date.now(),
|
||||
updated_at: overrides.updated_at ?? Date.now(),
|
||||
});
|
||||
|
||||
const buildCategory = (overrides: Partial<Category> = {}): Category => ({
|
||||
id: overrides.id ?? uuidv4(),
|
||||
name: overrides.name ?? 'Category',
|
||||
created_at: overrides.created_at ?? Date.now(),
|
||||
updated_at: overrides.updated_at ?? Date.now(),
|
||||
});
|
||||
|
||||
const createMockDb = (options: {
|
||||
areas?: Area[];
|
||||
products?: Product[];
|
||||
categories?: Category[];
|
||||
} = {}) => {
|
||||
const db = {
|
||||
areas: new MockTable<Area>(options.areas ?? []),
|
||||
products: new MockTable<Product>(options.products ?? []),
|
||||
categories: new MockTable<Category>(options.categories ?? []),
|
||||
pickLists: new MockTable<any>(),
|
||||
pickItems: new MockTable<any>(),
|
||||
} as unknown as StockFillDB;
|
||||
|
||||
return db;
|
||||
};
|
||||
|
||||
describe('seedDatabase', () => {
|
||||
it('deduplicates seeded areas, categories, and products', async () => {
|
||||
const duplicateSeedArea = buildArea({ name: seedAreas[0] });
|
||||
const trailingSpaceArea = buildArea({ name: `${seedAreas[0]} ` });
|
||||
const customArea = buildArea({ name: 'Produce' });
|
||||
|
||||
const duplicateProduct = buildProduct({ name: seedProducts[0].name, category: seedProducts[0].category });
|
||||
const duplicateProductWithWhitespace = buildProduct({ name: `${seedProducts[0].name} `, category: seedProducts[0].category });
|
||||
const customProduct = buildProduct({ name: 'Custom Item', category: 'Specials' });
|
||||
|
||||
const duplicateCategory = buildCategory({ name: seedCategories[0] });
|
||||
const trailingSpaceCategory = buildCategory({ name: `${seedCategories[0]} ` });
|
||||
|
||||
const db = createMockDb({
|
||||
areas: [duplicateSeedArea, trailingSpaceArea, customArea],
|
||||
products: [duplicateProduct, duplicateProductWithWhitespace, customProduct],
|
||||
categories: [duplicateCategory, trailingSpaceCategory],
|
||||
});
|
||||
|
||||
await seedDatabase(db);
|
||||
|
||||
const areas = await db.areas.toArray();
|
||||
const areaNames = areas.map((area) => normalizeName(area.name));
|
||||
const seededAreaNames = new Set(seedAreas.map(normalizeName));
|
||||
|
||||
expect(areaNames.filter((name) => name === normalizeName(seedAreas[0]))).toHaveLength(1);
|
||||
expect(new Set(areaNames.filter((name) => seededAreaNames.has(name)))).toEqual(seededAreaNames);
|
||||
expect(areaNames).toContain(normalizeName(customArea.name));
|
||||
|
||||
const products = await db.products.toArray();
|
||||
const seededProductNames = new Set(seedProducts.map((product) => normalizeName(product.name)));
|
||||
const productNamesInDb = products.map((product) => normalizeName(product.name));
|
||||
|
||||
expect(productNamesInDb.filter((name) => name === normalizeName(seedProducts[0].name))).toHaveLength(1);
|
||||
expect(new Set(productNamesInDb.filter((name) => seededProductNames.has(name)))).toEqual(
|
||||
seededProductNames,
|
||||
);
|
||||
expect(productNamesInDb).toContain(normalizeName(customProduct.name));
|
||||
|
||||
const categories = await db.categories.toArray();
|
||||
const seededCategoryNames = new Set(seedCategories.map(normalizeName));
|
||||
const categoryNamesInDb = categories.map((category) => normalizeName(category.name));
|
||||
|
||||
expect(categoryNamesInDb.filter((name) => name === normalizeName(seedCategories[0]))).toHaveLength(1);
|
||||
expect(new Set(categoryNamesInDb.filter((name) => seededCategoryNames.has(name)))).toEqual(
|
||||
seededCategoryNames,
|
||||
);
|
||||
});
|
||||
});
|
||||
-125
@@ -1,125 +0,0 @@
|
||||
import { Table } from 'dexie';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { StockFillDB } from './index';
|
||||
import { DEFAULT_BULK_NAME, DEFAULT_UNIT_TYPE } from '../models/Product';
|
||||
|
||||
const now = () => Date.now();
|
||||
|
||||
export const seedAreas = ['Drinks', 'Snacks', 'Dairy'];
|
||||
|
||||
export const seedProducts = [
|
||||
{ name: 'Nutrient Water Endurance', category: 'Drinks' },
|
||||
{ name: 'Nutrient Water Focus', category: 'Drinks' },
|
||||
{ name: 'Cocobella Choc', category: 'Drinks' },
|
||||
{ name: 'Cocobella straight', category: 'Drinks' },
|
||||
{ name: 'Cocobella watermelon', category: 'Drinks' },
|
||||
{ name: 'Cocoa Coast Chocolate', category: 'Drinks' },
|
||||
{ name: 'Cocoa Coast Mango', category: 'Drinks' },
|
||||
{ name: 'Cocoa Coast Lychee', category: 'Drinks' },
|
||||
{ name: 'Cocoa Coast Raspberry', category: 'Drinks' },
|
||||
{ name: 'Cocoa Coast Pasionfruit', category: 'Drinks' },
|
||||
{ name: 'Mount Franklin Sparkling Lime', category: 'Drinks' },
|
||||
{ name: 'Mount Franklin Sparkling', category: 'Drinks' },
|
||||
{ name: 'Mount Franklin 600ml', category: 'Drinks' },
|
||||
{ name: 'Nu 600ml', category: 'Drinks' },
|
||||
{ name: 'Mt Cooroy 600ml', category: 'Drinks' },
|
||||
{ name: 'Mt Coory 1L', category: 'Drinks' },
|
||||
{ name: 'Mt Cooroy 1.5L', category: 'Drinks' },
|
||||
{ name: 'Pump 750', category: 'Drinks' },
|
||||
{ name: 'Pump Lime', category: 'Drinks' },
|
||||
{ name: 'Pump Berry', category: 'Drinks' },
|
||||
{ name: 'Pump Watermelon', category: 'Drinks' },
|
||||
{ name: 'Mount Franklin 1.5L', category: 'Drinks' },
|
||||
{ name: 'Pump 1.5L', category: 'Drinks' },
|
||||
{ name: 'Nu 1.5L', category: 'Drinks' },
|
||||
{ name: 'Smiths Salt n Vinegar 90g', category: 'Chips' },
|
||||
{ name: 'Mars Bar', category: 'Chocolates' },
|
||||
];
|
||||
|
||||
export const seedCategories = Array.from(
|
||||
new Set(seedProducts.map(({ category }) => category)),
|
||||
).sort();
|
||||
|
||||
const normalizeName = (name: string) => name.trim().toLowerCase();
|
||||
|
||||
const dedupeSeedRecords = async <T extends { id: string; name: string }>(
|
||||
table: Table<T>,
|
||||
seededNames: Set<string>,
|
||||
) => {
|
||||
const existing = await table.toArray();
|
||||
const seen = new Set<string>();
|
||||
const duplicateIds: string[] = [];
|
||||
|
||||
existing.forEach((record) => {
|
||||
const normalized = normalizeName(record.name);
|
||||
if (!seededNames.has(normalized)) return;
|
||||
|
||||
if (seen.has(normalized)) {
|
||||
duplicateIds.push(record.id);
|
||||
return;
|
||||
}
|
||||
|
||||
seen.add(normalized);
|
||||
});
|
||||
|
||||
if (duplicateIds.length > 0) {
|
||||
await table.bulkDelete(duplicateIds);
|
||||
}
|
||||
|
||||
return seen;
|
||||
};
|
||||
|
||||
const buildProductRecord = (product: { name: string; category: string }) => ({
|
||||
id: uuidv4(),
|
||||
name: product.name,
|
||||
category: product.category,
|
||||
unit_type: DEFAULT_UNIT_TYPE,
|
||||
bulk_name: DEFAULT_BULK_NAME,
|
||||
archived: false,
|
||||
created_at: now(),
|
||||
updated_at: now(),
|
||||
});
|
||||
|
||||
export const seedDatabase = async (db: StockFillDB) => {
|
||||
const seededAreaNames = new Set(seedAreas.map(normalizeName));
|
||||
const existingSeedAreas = await dedupeSeedRecords(db.areas, seededAreaNames);
|
||||
const missingAreas = seedAreas.filter((area) => !existingSeedAreas.has(normalizeName(area)));
|
||||
|
||||
if (missingAreas.length > 0) {
|
||||
await db.areas.bulkAdd(
|
||||
missingAreas.map((name) => ({
|
||||
id: uuidv4(),
|
||||
name,
|
||||
created_at: now(),
|
||||
updated_at: now(),
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
const seededCategoryNames = new Set(seedCategories.map(normalizeName));
|
||||
const existingSeedCategories = await dedupeSeedRecords(db.categories, seededCategoryNames);
|
||||
const missingCategories = seedCategories.filter(
|
||||
(category) => !existingSeedCategories.has(normalizeName(category)),
|
||||
);
|
||||
|
||||
if (missingCategories.length > 0) {
|
||||
await db.categories.bulkAdd(
|
||||
missingCategories.map((category) => ({
|
||||
id: uuidv4(),
|
||||
name: category,
|
||||
created_at: now(),
|
||||
updated_at: now(),
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
const seededProductNames = new Set(seedProducts.map(({ name }) => normalizeName(name)));
|
||||
const existingSeedProducts = await dedupeSeedRecords(db.products, seededProductNames);
|
||||
const missingProducts = seedProducts.filter(
|
||||
(product) => !existingSeedProducts.has(normalizeName(product.name)),
|
||||
);
|
||||
|
||||
if (missingProducts.length > 0) {
|
||||
await db.products.bulkAdd(missingProducts.map(buildProductRecord));
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user