const test = require('node:test'); const assert = require('node:assert/strict'); const filaments = require('../lib/filaments'); const spool = (material, hex, name) => ({ id: hex, material, colour_hex: hex, colour: name || hex, remaining_g: 500 }); // The model that started this: four PLA slots, one spool of Jade White. const pooh = [ { type: 'PLA', colour: '#000000' }, { type: 'PLA', colour: '#586579' }, { type: 'PLA', colour: '#8E9089' }, { type: 'PLA', colour: '#FFFFFF' }, ]; test('a four-colour print needs those colours, not four spools of anything', () => { const shelf = [spool('PLA', '#ffffff', 'Jade White')]; const slots = filaments.assignSpools(pooh, shelf); assert.deepEqual(slots.map((s) => s.filled), [false, false, false, true]); assert.equal(slots[3].by, 'Jade White'); assert.equal(filaments.isPrintable(pooh, shelf), false); assert.equal(filaments.shortfall(pooh, shelf).length, 3); // Four white spools still can't print it: the other three colours are missing. const fourWhite = ['a', 'b', 'c', 'd'].map((k) => ({ ...spool('PLA', '#ffffff', 'Jade White'), id: k })); assert.equal(filaments.isPrintable(pooh, fourWhite), false); }); test('with every colour on the shelf it prints', () => { const shelf = [spool('PLA', '#000000', 'Black'), spool('PLA', '#586579', 'Blue Grey'), spool('PLA', '#8E9089', 'Grey'), spool('PLA', '#ffffff', 'Jade White')]; assert.equal(filaments.isPrintable(pooh, shelf), true); assert.deepEqual(filaments.assignSpools(pooh, shelf).map((s) => s.by), ['Black', 'Blue Grey', 'Grey', 'Jade White']); }); test('one spool cannot fill two slots of the same colour', () => { const twoBlack = [{ type: 'PLA', colour: '#000000' }, { type: 'PLA', colour: '#000000' }]; assert.equal(filaments.isPrintable(twoBlack, [spool('PLA', '#000000', 'Black')]), false); assert.equal(filaments.isPrintable(twoBlack, [spool('PLA', '#000000', 'Black'), { ...spool('PLA', '#000000', 'Black 2'), id: 'b2' }]), true); }); test('8-digit hex with alpha parses as its colour', () => { assert.deepEqual(filaments.rgb('#FFFFFFFF'), { r: 255, g: 255, b: 255 }); assert.deepEqual(filaments.rgb('#586579'), { r: 88, g: 101, b: 121 }); assert.equal(filaments.rgb('not a colour'), null); // A slot written with alpha still matches a plain spool of that colour. assert.equal(filaments.isPrintable([{ type: 'PLA', colour: '#FFFFFFFF' }], [spool('PLA', '#ffffff', 'Jade White')]), true); }); test('near shades count, but near-black is not black', () => { const nearWhite = [{ type: 'PLA', colour: '#FDFDFD' }]; assert.equal(filaments.isPrintable(nearWhite, [spool('PLA', '#ffffff', 'Jade White')]), true); // #161616 sits about 66 away from black, beyond the tolerance of 50. assert.ok(filaments.colourDistance('#000000', '#161616') > filaments.COLOUR_TOLERANCE); assert.equal(filaments.isPrintable([{ type: 'PLA', colour: '#161616' }], [spool('PLA', '#000000', 'Black')]), false); }); test('the nearest spool is named when a slot can’t be filled', () => { const [slot] = filaments.assignSpools([{ type: 'PLA', colour: '#000000' }], [spool('PLA', '#ffffff', 'Jade White')]); assert.equal(slot.filled, false); assert.equal(slot.nearest.name, 'Jade White'); assert.ok(slot.nearest.distance > 0); }); test('material still matters, and a slot with no colour takes any spool of it', () => { assert.equal(filaments.isPrintable([{ type: 'PETG', colour: '#ffffff' }], [spool('PLA', '#ffffff', 'Jade White')]), false); assert.equal(filaments.isPrintable([{ type: 'PLA', colour: null }], [spool('PLA', '#123456', 'Anything')]), true); }); test('an empty spool doesn’t count, but an unweighed one does', () => { const one = [{ type: 'PLA', colour: '#ffffff' }]; assert.equal(filaments.isPrintable(one, [{ ...spool('PLA', '#ffffff', 'Jade White'), remaining_g: 0 }]), false); assert.equal(filaments.isPrintable(one, [{ ...spool('PLA', '#ffffff', 'Jade White'), remaining_g: null }]), true); }); test('a model we know nothing about is not claimed to be printable', () => { assert.equal(filaments.isPrintable(null, [spool('PLA', '#ffffff')]), false); assert.equal(filaments.isPrintable([], [spool('PLA', '#ffffff')]), false); assert.deepEqual(filaments.assignSpools(null, []), []); }); test('slots sent from the page are cleaned before they reach the database', () => { const cleaned = filaments.cleanSlots([ { type: ' pla ', colour: '#ffffff' }, // trimmed, hex upper-cased { type: 'PETG', colour: 'not a colour' }, // bad colour becomes "any colour" { type: '', colour: '#000000' }, // no material: dropped entirely { type: 'PLA', colour: '#8e9089' }, ]); assert.deepEqual(cleaned, [ { type: 'pla', colour: '#FFFFFF' }, { type: 'PETG', colour: null }, { type: 'PLA', colour: '#8E9089' }, ]); }); test('anything that isn’t a list of slots is refused outright', () => { assert.equal(filaments.cleanSlots(null), null); assert.equal(filaments.cleanSlots('PLA'), null); assert.equal(filaments.cleanSlots({ type: 'PLA' }), null); assert.deepEqual(filaments.cleanSlots([]), []); }); test('"any colour" survives the trip from the page to the database', () => { // What the editor sends when the box is ticked, plus the two shapes that can // arrive from older clients or a hand-made request. assert.deepEqual(filaments.cleanSlots([ { type: 'PLA', colour: null }, { type: 'PETG', colour: '' }, { type: 'ABS' }, ]), [ { type: 'PLA', colour: null }, { type: 'PETG', colour: null }, { type: 'ABS', colour: null }, ]); }); test('a slot with no colour is filled by any spool of that material', () => { const anyWhite = [{ type: 'PLA', colour: null }]; assert.equal(filaments.isPrintable(anyWhite, [{ id: 1, material: 'PLA', colour_hex: '#123456', colour: 'Anything', remaining_g: 200 }]), true); // …but still not by the wrong material, and still one spool per slot. assert.equal(filaments.isPrintable(anyWhite, [{ id: 1, material: 'PETG', colour_hex: '#123456', colour: 'Anything', remaining_g: 200 }]), false); assert.equal(filaments.isPrintable([{ type: 'PLA', colour: null }, { type: 'PLA', colour: null }], [{ id: 1, material: 'PLA', colour_hex: '#123456', colour: 'Anything', remaining_g: 200 }]), false); }); test('an "any colour" slot takes what’s left, not what another slot needs', () => { // Reported from the live library: editing slot 2 of a bookmark to "any // colour" stole the one white spool, leaving the slot that actually wanted // #FFFFFF empty and the verdict no better than before. const shelf = [{ id: 1, material: 'PLA', colour_hex: '#FFFFFF', colour: 'Jade White', remaining_g: 1000 }]; const slots = filaments.assignSpools([{ type: 'PLA', colour: null }, { type: 'PLA', colour: '#FFFFFF' }], shelf); assert.equal(slots[1].filled, true, 'the slot that asks for white gets the white spool'); assert.equal(slots[1].by, 'Jade White'); assert.equal(slots[0].filled, false, 'the any-colour slot goes without'); // With two spools, both are filled and the exact match still gets white. const two = shelf.concat({ id: 2, material: 'PLA', colour_hex: '#123456', colour: 'Anything', remaining_g: 500 }); const both = filaments.assignSpools([{ type: 'PLA', colour: null }, { type: 'PLA', colour: '#FFFFFF' }], two); assert.deepEqual(both.map((s) => s.by), ['Anything', 'Jade White']); });