Skip to content

Commit af2bb0f

Browse files
authored
Merge pull request #231 from htilly/develop
v2.3.0: Consolidate modules, add handlers, improve tests and validation
2 parents 207c1c3 + 0e4a436 commit af2bb0f

3 files changed

Lines changed: 153 additions & 6 deletions

File tree

lib/command-handlers.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,10 @@ async function searchalbum(input, channel) {
645645

646646
let message = `Found ${sortedAlbums.length} albums:\n`;
647647
sortedAlbums.forEach((albumResult) => {
648-
message += `> *${albumResult.name}* by _${albumResult.artist}_\n`;
648+
const trackInfo = albumResult.totalTracks
649+
? ` (${albumResult.totalTracks} ${albumResult.totalTracks === 1 ? 'track' : 'tracks'})`
650+
: '';
651+
message += `> *${albumResult.name}* by _${albumResult.artist}_${trackInfo}\n`;
649652
});
650653
sendMessage(message, channel);
651654
} catch (err) {

lib/spotify.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,8 @@ module.exports = function (config, injectedLogger) {
303303
name: album.name,
304304
artist: album.artists[0].name,
305305
uri: album.uri,
306-
popularity: album.popularity // Keep popularity for sorting
306+
popularity: album.popularity, // Keep popularity for sorting
307+
totalTracks: album.total_tracks || 0
307308
}));
308309
},
309310

test/tools/integration-test-suite.mjs

Lines changed: 147 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -492,14 +492,27 @@ class TestCase {
492492

493493
// Validators
494494
const queueSizeStore = {};
495+
const extractedValues = {};
495496

496497
function extractQueueSize(responses) {
497498
const allText = responses.map(r => r.text).join(' ');
499+
// Match "X tracks" pattern first (more specific)
500+
const tracksMatch = allText.match(/(\d+)\s*track/i);
501+
if (tracksMatch) return parseInt(tracksMatch[1], 10);
502+
// Fall back to first number
498503
const match = allText.match(/\b(\d+)\b/);
499504
if (!match) return null;
500505
return parseInt(match[1], 10);
501506
}
502507

508+
function extractTrackCountFromResponse(responses) {
509+
const allText = responses.map(r => r.text).join(' ');
510+
// Match patterns like "(150 tracks)" or "150 tracks"
511+
const match = allText.match(/\((\d+)\s*tracks?\)/i) || allText.match(/(\d+)\s*tracks?/i);
512+
if (!match) return null;
513+
return parseInt(match[1], 10);
514+
}
515+
503516
const validators = {
504517
containsText: (text) => (responses) => {
505518
const allText = responses.map(r => r.text).join(' ');
@@ -568,6 +581,72 @@ const validators = {
568581
}
569582
if (size >= baseline + minIncrease) return true;
570583
return `Expected queue size to increase by ${minIncrease} from ${baseline}, got ${size}`;
584+
},
585+
586+
// Verify queue size increased by EXACTLY N (not double, not less)
587+
queueSizeIncreaseExactly: (key, exactIncrease) => (responses) => {
588+
const size = extractQueueSize(responses);
589+
if (size === null) return 'Could not parse queue size from response';
590+
const baseline = queueSizeStore[key];
591+
if (baseline === undefined || baseline === null) {
592+
return `No baseline queue size recorded for "${key}"`;
593+
}
594+
const actualIncrease = size - baseline;
595+
596+
// Exact match - no output
597+
if (actualIncrease === exactIncrease) return true;
598+
599+
// Disable tolerance for very small expected increases (1-2 tracks),
600+
// where a "90%" tolerance would allow clearly incorrect results (e.g., 0 of 1).
601+
if (exactIncrease >= 3) {
602+
const tolerance = Math.floor(exactIncrease * 0.9);
603+
if (actualIncrease >= tolerance && actualIncrease < exactIncrease) {
604+
console.log(` ⚠️ WARNING: Queue increased by ${actualIncrease} (expected ${exactIncrease}, baseline: ${baseline}${size})`);
605+
return true;
606+
}
607+
}
608+
609+
// Outside tolerance or small exact value - fail
610+
return `❌ FAIL: Queue increased by ${actualIncrease} (expected exactly ${exactIncrease}, baseline: ${baseline}${size})`;
611+
},
612+
613+
// Extract and store track count from search results (e.g., "(50 tracks)")
614+
extractAndStoreTrackCount: (key) => (responses) => {
615+
const count = extractTrackCountFromResponse(responses);
616+
if (count === null) return 'Could not extract track count from response';
617+
extractedValues[key] = count;
618+
return true;
619+
},
620+
621+
// Verify queue increased by stored track count (with tolerance for duplicates/blacklist)
622+
queueSizeIncreasedByStoredCount: (baselineKey, countKey, tolerancePercent = 10) => (responses) => {
623+
const size = extractQueueSize(responses);
624+
if (size === null) return 'Could not parse queue size from response';
625+
const baseline = queueSizeStore[baselineKey];
626+
const expectedCount = extractedValues[countKey];
627+
if (baseline === undefined) return `No baseline recorded for "${baselineKey}"`;
628+
if (expectedCount === undefined) return `No track count recorded for "${countKey}"`;
629+
630+
const actualIncrease = size - baseline;
631+
const minExpected = Math.floor(expectedCount * (1 - tolerancePercent / 100));
632+
const maxExpected = expectedCount; // Should not exceed expected (no doubling!)
633+
634+
// Exact match - no output
635+
if (actualIncrease === expectedCount) return true;
636+
637+
// Within tolerance but not exact - warning but pass
638+
if (actualIncrease >= minExpected && actualIncrease < expectedCount) {
639+
console.log(` ⚠️ WARNING: Queue increased by ${actualIncrease} (expected ${expectedCount}, baseline: ${baseline}${size}, tolerance: ${minExpected}-${maxExpected})`);
640+
return true;
641+
}
642+
643+
// Exceeded expected (possible doubling bug) - fail
644+
if (actualIncrease > maxExpected) {
645+
return `❌ FAIL: Queue increased by ${actualIncrease} but expected max ${maxExpected} - possible DUPLICATE QUEUEING BUG! (baseline: ${baseline}${size})`;
646+
}
647+
648+
// Below minimum (too many filtered) - fail
649+
return `❌ FAIL: Queue increased by ${actualIncrease}, expected ${minExpected}-${maxExpected} (based on ${expectedCount} tracks, baseline: ${baseline}${size})`;
571650
}
572651
};
573652

@@ -730,6 +809,17 @@ const testSuiteArray = [
730809
// PHASE 4: BUILD UP THE QUEUE (add tracks for later tests)
731810
// ═══════════════════════════════════════════════════════════════════
732811

812+
// Get baseline queue size before adding tracks
813+
new TestCase(
814+
'Queue Size - Initial Baseline',
815+
'size',
816+
validators.and(
817+
validators.responseCount(1, 2),
818+
validators.recordQueueSize('initialBaseline')
819+
),
820+
4
821+
),
822+
733823
new TestCase(
734824
'Add Track #1 - Foo Fighters',
735825
'add Foo Fighters - Best Of You',
@@ -744,6 +834,18 @@ const testSuiteArray = [
744834
7
745835
),
746836

837+
// Verify exactly 1 track was added
838+
new TestCase(
839+
'Queue Size - After Track #1 (+1)',
840+
'size',
841+
validators.and(
842+
validators.responseCount(1, 2),
843+
validators.queueSizeIncreaseExactly('initialBaseline', 1),
844+
validators.recordQueueSize('afterTrack1')
845+
),
846+
4
847+
),
848+
747849
new TestCase(
748850
'Add Track - Duplicate Detection',
749851
'add Foo Fighters - Best Of You',
@@ -768,6 +870,18 @@ const testSuiteArray = [
768870
7
769871
),
770872

873+
// Verify exactly 1 more track added (total +2 from initial)
874+
new TestCase(
875+
'Queue Size - After Track #2 (+1)',
876+
'size',
877+
validators.and(
878+
validators.responseCount(1, 2),
879+
validators.queueSizeIncreaseExactly('afterTrack1', 1),
880+
validators.recordQueueSize('afterTrack2')
881+
),
882+
4
883+
),
884+
771885
new TestCase(
772886
'Add Track #3 - Queen',
773887
'add Queen - Bohemian Rhapsody',
@@ -796,6 +910,21 @@ const testSuiteArray = [
796910
7
797911
),
798912

913+
// Search album first to get track count
914+
new TestCase(
915+
'Search Album - Abbey Road (get track count)',
916+
'searchalbum abbey road',
917+
validators.and(
918+
validators.responseCount(1, 2),
919+
validators.or(
920+
validators.containsText('Beatles'),
921+
validators.containsText('Abbey Road')
922+
),
923+
validators.extractAndStoreTrackCount('abbeyRoadTracks')
924+
),
925+
5
926+
),
927+
799928
new TestCase(
800929
'Queue Size - Baseline Before Album',
801930
'size',
@@ -820,17 +949,30 @@ const testSuiteArray = [
820949
10
821950
),
822951

952+
// Verify album tracks were added (not doubled!)
823953
new TestCase(
824-
'Queue Size - After Album',
954+
'Queue Size - After Album (verify no doubling)',
825955
'size',
826956
validators.and(
827957
validators.responseCount(1, 2),
828-
validators.queueSizeIncreaseFrom('beforeAlbum', 1),
958+
validators.queueSizeIncreasedByStoredCount('beforeAlbum', 'abbeyRoadTracks', 20),
829959
validators.recordQueueSize('beforePlaylist')
830960
),
831961
4
832962
),
833963

964+
// Search playlist first to get track count
965+
new TestCase(
966+
'Search Playlist - Rock Classics (get track count)',
967+
'searchplaylist rock classics',
968+
validators.and(
969+
validators.responseCount(1, 2),
970+
validators.matchesRegex(/playlist|tracks|\d+/i),
971+
validators.extractAndStoreTrackCount('rockClassicsTracks')
972+
),
973+
5
974+
),
975+
834976
new TestCase(
835977
'Add Playlist - Rock Classics',
836978
'addplaylist rock classics',
@@ -845,12 +987,13 @@ const testSuiteArray = [
845987
12
846988
),
847989

990+
// Verify playlist tracks added (not doubled!)
848991
new TestCase(
849-
'Queue Size - After Playlist',
992+
'Queue Size - After Playlist (verify no doubling)',
850993
'size',
851994
validators.and(
852995
validators.responseCount(1, 2),
853-
validators.queueSizeIncreaseFrom('beforePlaylist', 1)
996+
validators.queueSizeIncreasedByStoredCount('beforePlaylist', 'rockClassicsTracks', 20)
854997
),
855998
4
856999
),

0 commit comments

Comments
 (0)