exhaustive tests for sequencer function
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
"main": "distribution/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node distribution/index.js",
|
||||
"start": "node distribution/src/index.js",
|
||||
"test": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { OpenWeatherAPI, type CurrentWeather } from 'openweather-api-node';
|
||||
import { OpenWeatherAPI, type DailyWeather } from 'openweather-api-node';
|
||||
import { voiceLines } from './voice.js';
|
||||
import type { Config } from './index.js';
|
||||
import type { Voice } from './voice.js';
|
||||
@@ -19,7 +19,7 @@ function selectOne<T>(arr: T[]): T {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
}
|
||||
|
||||
function resolveSide(side: string, currentWeather: CurrentWeather) {
|
||||
function resolveSide(side: string, currentWeather: DailyWeather) {
|
||||
if (!side.startsWith('weather')) {
|
||||
return side.includes('.') ? parseFloat(side) : parseInt(side);
|
||||
}
|
||||
@@ -30,13 +30,23 @@ function resolveSide(side: string, currentWeather: CurrentWeather) {
|
||||
return typeof w === 'object' ? JSON.stringify(w) : w as (string | number);
|
||||
}
|
||||
|
||||
function conditionIsMet(condition: string | undefined, currentWeather: CurrentWeather): boolean {
|
||||
function notNotANumber(something: number | string, defaultVal: string) {
|
||||
if (typeof something === 'string' || !isNaN(something)) {
|
||||
return something;
|
||||
}
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
function conditionIsMet(condition: string | undefined, currentWeather: DailyWeather): boolean {
|
||||
if (typeof condition !== 'string') {
|
||||
return true;
|
||||
}
|
||||
const [lhs, relational, rhs] = condition.split(' ');
|
||||
const lhsResolved = resolveSide(lhs, currentWeather);
|
||||
const rhsResolved = resolveSide(rhs, currentWeather);
|
||||
if (lhs === undefined || relational === undefined || rhs === undefined) {
|
||||
throw new Error(`Condition "${condition}" is not in the correct format`);
|
||||
}
|
||||
const lhsResolved = notNotANumber(resolveSide(lhs, currentWeather), lhs);
|
||||
const rhsResolved = notNotANumber(resolveSide(rhs, currentWeather), rhs);
|
||||
switch (relational) {
|
||||
case '=':
|
||||
case '==':
|
||||
@@ -56,7 +66,7 @@ function conditionIsMet(condition: string | undefined, currentWeather: CurrentWe
|
||||
}
|
||||
}
|
||||
|
||||
function resolveMacro(str: string, currentWeather: CurrentWeather): string[] {
|
||||
function resolveMacro(str: string, currentWeather: DailyWeather): string[] {
|
||||
if (str.startsWith('%')) {
|
||||
const [profile, subject] = str.substring(1).split(' ', 2);
|
||||
const voiceProfile: Voice = config.voices[profile];
|
||||
@@ -64,18 +74,15 @@ function resolveMacro(str: string, currentWeather: CurrentWeather): string[] {
|
||||
subject.split('.').forEach(t => resolvedSubject = resolvedSubject[t]);
|
||||
return voiceLines(voiceProfile, resolvedSubject);
|
||||
}
|
||||
else if (str.startsWith('$')) {
|
||||
return null;
|
||||
}
|
||||
return [str];
|
||||
}
|
||||
|
||||
function processSequence(sequence: Sequence, currentWeather: CurrentWeather): string[] {
|
||||
function processSequence(sequence: Sequence, currentWeather: DailyWeather): string[] {
|
||||
const tracks = sequence.tracks;
|
||||
return tracks.map(t => resolveMacro(t, currentWeather)).flat().filter(t => t !== null);
|
||||
}
|
||||
|
||||
function processSegment(segment: SegmentName, currentWeather: CurrentWeather): string[] {
|
||||
function processSegment(segment: SegmentName, currentWeather: DailyWeather): string[] {
|
||||
if (!(segment in config.segments)) {
|
||||
return (config.sequences[segment].conditions || []).every(c => conditionIsMet(c, currentWeather)) ? processSequence(config.sequences[segment], currentWeather) : [];
|
||||
}
|
||||
@@ -89,8 +96,7 @@ function processSegment(segment: SegmentName, currentWeather: CurrentWeather): s
|
||||
async function Sequencer(conf: Config): Promise<string[]> {
|
||||
config = conf;
|
||||
const weather = new OpenWeatherAPI(conf.weather);
|
||||
const currentWeather = await weather.getCurrent();
|
||||
console.log(JSON.stringify(currentWeather));
|
||||
const currentWeather = await weather.getToday();
|
||||
const sequence: string[] = [];
|
||||
const program: SegmentName[] = selectOne(conf.programs);
|
||||
for (let i = 0; i < program.length; i++) {
|
||||
|
@@ -9,7 +9,7 @@ vi.mock('openweather-api-node', () => {
|
||||
return {
|
||||
OpenWeatherAPI: vi.fn().mockImplementation((_) => {
|
||||
return {
|
||||
getCurrent: vi.fn(() => {
|
||||
getToday: vi.fn(() => {
|
||||
return {
|
||||
"lat": 43.0748,
|
||||
"lon": -89.3838,
|
||||
@@ -106,5 +106,85 @@ describe('sequencer', () => {
|
||||
weather: dummyWeather
|
||||
})).to.be.ordered.members(['seq1.flac', 'seq1.flac']);
|
||||
});
|
||||
|
||||
it('throws an error on invalid conditions', async () => {
|
||||
await expect(Sequencer({
|
||||
programs: [['sequence 1']],
|
||||
segments: {
|
||||
},
|
||||
sequences: {
|
||||
'sequence 1': {
|
||||
'conditions': ['100'],
|
||||
'tracks': [
|
||||
'seq1.flac'
|
||||
]
|
||||
}
|
||||
},
|
||||
voices: {},
|
||||
weather: dummyWeather
|
||||
})).rejects.toThrow(/not in the correct format/);
|
||||
|
||||
await expect(Sequencer({
|
||||
programs: [['sequence 1']],
|
||||
segments: {
|
||||
},
|
||||
sequences: {
|
||||
'sequence 1': {
|
||||
'conditions': ['1 ~ 2'],
|
||||
'tracks': [
|
||||
'seq1.flac'
|
||||
]
|
||||
}
|
||||
},
|
||||
voices: {},
|
||||
weather: dummyWeather
|
||||
})).rejects.toThrow(/Unsupported relational operator/);
|
||||
});
|
||||
|
||||
it('can stringify conditions', async () => {
|
||||
expect(await Sequencer({
|
||||
programs: [['sequence 1']],
|
||||
segments: {
|
||||
},
|
||||
sequences: {
|
||||
'sequence 1': {
|
||||
'conditions': ['weather.feelsLike = {"cur":55.31}'],
|
||||
'tracks': [
|
||||
'seq1.flac'
|
||||
]
|
||||
}
|
||||
},
|
||||
voices: {},
|
||||
weather: dummyWeather
|
||||
})).to.be.ordered.members(['seq1.flac']);
|
||||
});
|
||||
|
||||
it('can parse voice macros', async () => {
|
||||
expect(await Sequencer({
|
||||
programs: [['sequence 1']],
|
||||
segments: {
|
||||
},
|
||||
sequences: {
|
||||
'sequence 1': {
|
||||
'tracks': [
|
||||
'%alice weather.temp.max'
|
||||
]
|
||||
}
|
||||
},
|
||||
voices: {
|
||||
"alice": {
|
||||
"directory": "alice/",
|
||||
"extension": "flac"
|
||||
}
|
||||
},
|
||||
weather: dummyWeather
|
||||
})).to.be.ordered.members([
|
||||
'alice/fifty.flac',
|
||||
'alice/eight.flac',
|
||||
'alice/point.flac',
|
||||
'alice/zero.flac',
|
||||
'alice/one.flac'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user