printhive/test/filaments.test.js
dlawler489 ba3bb415bb Track the filament you have, and what each model asks for
A Bambu Studio or OrcaSlicer 3MF names the filaments its plates use, but
reading that means shelling out to Python, so it can't be done per page load.
A background pass — in the same loop as thumbnails and fingerprints — reads it
once per model into models.filaments, which turns "what can I print now" into
a comparison of two lists.

Adds a Filament page for the spools on hand: material, colour, brand and grams
left, edited in place. Matching is on material only; a project's exact colours
would make almost nothing printable, and a different colour still prints.

Measured on the dev library: 10 models read with no errors, the two Bambu
projects resolving to four PLA colours each, and no plate images written
because the reader skips --images and queues behind anything interactive.

Not done yet: nothing in the library view uses this — no "printable now" badge
or filter — and the Filament page itself has not been seen rendered, because
the local preview browser has no session.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 11:18:54 +10:00

46 lines
2.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const test = require('node:test');
const assert = require('node:assert/strict');
const filaments = require('../lib/filaments');
const spools = (...materials) => materials.map((m, i) => ({ id: i + 1, material: m, remaining_g: 500 }));
test('a model is printable when every filament it names is on a spool', () => {
const project = [{ type: 'PLA', colour: '#00AE42' }, { type: 'PETG', colour: '#000000' }];
assert.equal(filaments.isPrintable(project, spools('PLA', 'PETG', 'ABS')), true);
assert.deepEqual(filaments.missingMaterials(project, spools('PLA', 'PETG')), []);
});
test('what is missing is named, not just refused', () => {
const project = [{ type: 'PLA' }, { type: 'PETG' }, { type: 'TPU' }];
assert.deepEqual(filaments.missingMaterials(project, spools('PLA')), ['PETG', 'TPU']);
assert.equal(filaments.isPrintable(project, spools('PLA')), false);
});
test('colour is ignored — a different colour still prints', () => {
const project = [{ type: 'PLA', colour: '#FF0000' }];
assert.equal(filaments.isPrintable(project, [{ material: 'PLA', colour_hex: '#0000FF', remaining_g: 200 }]), true);
});
test('case and spacing don’t matter, but a different material does', () => {
assert.equal(filaments.normalise(' pla basic '), 'PLA BASIC');
// PLA-CF is carbon filled: a plain PLA spool is not a substitute.
assert.deepEqual(filaments.missingMaterials([{ type: 'PLA-CF' }], spools('PLA')), ['PLA-CF']);
assert.deepEqual(filaments.missingMaterials([{ type: 'pla' }], spools('PLA')), []);
});
test('an empty spool doesn’t count, but an unweighed one does', () => {
const project = [{ type: 'PLA' }];
assert.deepEqual(filaments.missingMaterials(project, [{ material: 'PLA', remaining_g: 0 }]), ['PLA']);
assert.deepEqual(filaments.missingMaterials(project, [{ material: 'PLA', remaining_g: null }]), []);
assert.deepEqual(filaments.missingMaterials(project, [{ material: 'PLA' }]), []);
});
test('a model we know nothing about is not claimed to be printable', () => {
assert.equal(filaments.isPrintable(null, spools('PLA')), false);
assert.equal(filaments.isPrintable([], spools('PLA')), false);
});
test('the same material twice is asked for once', () => {
const project = [{ type: 'PLA', colour: '#FFF' }, { type: 'PLA', colour: '#000' }];
assert.deepEqual(filaments.missingMaterials(project, []), ['PLA']);
});