diff --git a/src/db.js b/src/db.js new file mode 100644 index 0000000..d771c31 --- /dev/null +++ b/src/db.js @@ -0,0 +1,58 @@ +const Database = require('better-sqlite3'); +const fs = require('node:fs'); +const path = require('node:path'); + +const SCHEMA = ` +CREATE TABLE IF NOT EXISTS products ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + url TEXT NOT NULL, + last_price REAL, + last_checked_at TEXT, + last_check_status TEXT, + created_at TEXT NOT NULL +)`; + +function openDb(dbPath) { + if (dbPath !== ':memory:') { + fs.mkdirSync(path.dirname(dbPath), { recursive: true }); + } + const db = new Database(dbPath); + db.exec(SCHEMA); + return db; +} + +function listProducts(db) { + return db.prepare('SELECT * FROM products ORDER BY created_at ASC').all(); +} + +function getProduct(db, id) { + return db.prepare('SELECT * FROM products WHERE id = ?').get(id); +} + +function addProduct(db, { name, url }) { + const createdAt = new Date().toISOString(); + const info = db.prepare( + 'INSERT INTO products (name, url, created_at) VALUES (?, ?, ?)' + ).run(name, url, createdAt); + return getProduct(db, info.lastInsertRowid); +} + +function deleteProduct(db, id) { + db.prepare('DELETE FROM products WHERE id = ?').run(id); +} + +function recordCheckSuccess(db, id, { price, checkedAt }) { + db.prepare( + 'UPDATE products SET last_price = ?, last_checked_at = ?, last_check_status = ? WHERE id = ?' + ).run(price, checkedAt, 'ok', id); +} + +function recordCheckFailure(db, id, status) { + db.prepare('UPDATE products SET last_check_status = ? WHERE id = ?').run(status, id); +} + +module.exports = { + openDb, listProducts, getProduct, addProduct, deleteProduct, + recordCheckSuccess, recordCheckFailure, +}; diff --git a/test/db.test.js b/test/db.test.js new file mode 100644 index 0000000..f463dca --- /dev/null +++ b/test/db.test.js @@ -0,0 +1,64 @@ +const test = require('node:test'); +const assert = require('node:assert/strict'); +const { + openDb, listProducts, getProduct, addProduct, deleteProduct, + recordCheckSuccess, recordCheckFailure, +} = require('../src/db'); + +function freshDb() { + return openDb(':memory:'); +} + +test('addProduct inserts a row with defaults and returns it', () => { + const db = freshDb(); + const product = addProduct(db, { name: 'Voorbeeld', url: 'https://example.com' }); + assert.equal(product.name, 'Voorbeeld'); + assert.equal(product.url, 'https://example.com'); + assert.equal(product.last_price, null); + assert.equal(product.last_checked_at, null); + assert.ok(product.created_at); + assert.ok(product.id); +}); + +test('listProducts returns all inserted products in insertion order', () => { + const db = freshDb(); + addProduct(db, { name: 'A', url: 'https://a' }); + addProduct(db, { name: 'B', url: 'https://b' }); + const products = listProducts(db); + assert.equal(products.length, 2); + assert.equal(products[0].name, 'A'); + assert.equal(products[1].name, 'B'); +}); + +test('getProduct returns undefined for an unknown id', () => { + const db = freshDb(); + assert.equal(getProduct(db, 999), undefined); +}); + +test('deleteProduct removes the row', () => { + const db = freshDb(); + const product = addProduct(db, { name: 'A', url: 'https://a' }); + deleteProduct(db, product.id); + assert.equal(getProduct(db, product.id), undefined); +}); + +test('recordCheckSuccess updates price, timestamp and status', () => { + const db = freshDb(); + const product = addProduct(db, { name: 'A', url: 'https://a' }); + recordCheckSuccess(db, product.id, { price: 12.5, checkedAt: '2026-09-05T08:00:00.000Z' }); + const updated = getProduct(db, product.id); + assert.equal(updated.last_price, 12.5); + assert.equal(updated.last_checked_at, '2026-09-05T08:00:00.000Z'); + assert.equal(updated.last_check_status, 'ok'); +}); + +test('recordCheckFailure only updates the status, leaving price and timestamp untouched', () => { + const db = freshDb(); + const product = addProduct(db, { name: 'A', url: 'https://a' }); + recordCheckSuccess(db, product.id, { price: 12.5, checkedAt: '2026-09-05T08:00:00.000Z' }); + recordCheckFailure(db, product.id, 'fetch_error'); + const updated = getProduct(db, product.id); + assert.equal(updated.last_price, 12.5); + assert.equal(updated.last_checked_at, '2026-09-05T08:00:00.000Z'); + assert.equal(updated.last_check_status, 'fetch_error'); +});