Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion templates/help/helpText.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
> `flushvote` - Vote to clear the entire queue. Needs *{{flushVoteLimit}}* votes within *{{voteTimeLimitMinutes}}* min. πŸ—‘οΈ

*πŸ“ Feedback:*
> `featurerequest <feature description>` - Create a GitHub issue for a feature request. ✨
> `featurerequest` (alias: `fr`) `<feature description>` - Create a GitHub issue for a feature request. ✨
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This help text change documents fr as an alias, but this PR doesn’t include the corresponding command registration change in index.js. Either add fr as an alias for featurerequest in the command registry (so users can actually invoke it) or avoid documenting the alias until it is implemented, otherwise help output will be misleading.

Suggested change
> `featurerequest` (alias: `fr`) `<feature description>` - Create a GitHub issue for a feature request. ✨
> `featurerequest` `<feature description>` - Create a GitHub issue for a feature request. ✨

Copilot uses AI. Check for mistakes.

_Tip: You can use Spotify URIs (spotify:track:...) OR paste Spotify links (https://open.spotify.com/...) with add, append, addalbum, and addplaylist commands!_

Expand Down
29 changes: 29 additions & 0 deletions test/aicode-agent.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,26 @@ describe('Feature Request Integration', function() {
// Check handler function exists
expect(content).to.include('async function _featurerequest(input, channel, userName)');
});

it('should have "fr" alias registered pointing to the same handler as featurerequest', function() {
const indexPath = path.join(process.cwd(), 'index.js');
const content = fs.readFileSync(indexPath, 'utf8');

// Check that 'fr' alias is registered with the same handler
expect(content).to.include("['fr', { fn: _featurerequest");
});

it('should register "fr" and "featurerequest" with the same handler function', function() {
const indexPath = path.join(process.cwd(), 'index.js');
const content = fs.readFileSync(indexPath, 'utf8');

// Both should reference _featurerequest as their handler
const featureRequestMatch = content.includes("['featurerequest', { fn: _featurerequest");
const frMatch = content.includes("['fr', { fn: _featurerequest");

expect(featureRequestMatch).to.be.true;
expect(frMatch).to.be.true;
Comment on lines +26 to +43
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These assertions expect a separate commandRegistry entry for fr ("['fr', { fn: _featurerequest"), but index.js resolves aliases via the per-command aliases array + aliasMap and currently has no fr key. This test will fail as-is and also doesn't match the project’s alias mechanism; update the implementation to add fr to featurerequest's aliases and update this test accordingly (e.g., assert aliases includes fr or that aliasMap contains it).

Suggested change
it('should have "fr" alias registered pointing to the same handler as featurerequest', function() {
const indexPath = path.join(process.cwd(), 'index.js');
const content = fs.readFileSync(indexPath, 'utf8');
// Check that 'fr' alias is registered with the same handler
expect(content).to.include("['fr', { fn: _featurerequest");
});
it('should register "fr" and "featurerequest" with the same handler function', function() {
const indexPath = path.join(process.cwd(), 'index.js');
const content = fs.readFileSync(indexPath, 'utf8');
// Both should reference _featurerequest as their handler
const featureRequestMatch = content.includes("['featurerequest', { fn: _featurerequest");
const frMatch = content.includes("['fr', { fn: _featurerequest");
expect(featureRequestMatch).to.be.true;
expect(frMatch).to.be.true;
it('should have "fr" alias registered on the featurerequest command', function() {
const indexPath = path.join(process.cwd(), 'index.js');
const content = fs.readFileSync(indexPath, 'utf8');
// Check that 'fr' is declared via the command alias mechanism
expect(content).to.match(/\['featurerequest',\s*\{\s*fn:\s*_featurerequest[\s\S]*?aliases:\s*\[[^\]]*['"]fr['"][^\]]*\]/);
});
it('should register "featurerequest" with _featurerequest and include "fr" in its aliases', function() {
const indexPath = path.join(process.cwd(), 'index.js');
const content = fs.readFileSync(indexPath, 'utf8');
// The command should use _featurerequest as its handler and expose 'fr' as an alias
const featureRequestMatch = /\['featurerequest',\s*\{\s*fn:\s*_featurerequest/.test(content);
const frAliasMatch = /\['featurerequest',\s*\{\s*fn:\s*_featurerequest[\s\S]*?aliases:\s*\[[^\]]*['"]fr['"][^\]]*\]/.test(content);
expect(featureRequestMatch).to.be.true;
expect(frAliasMatch).to.be.true;

Copilot uses AI. Check for mistakes.
});
});

describe('GitHub Issue Creation', function() {
Expand Down Expand Up @@ -56,5 +76,14 @@ describe('Feature Request Integration', function() {

expect(content).to.include('featurerequest');
});

it('should document the "fr" alias in the standard help text', function() {
const helpPath = path.join(process.cwd(), 'templates', 'help', 'helpText.txt');
const content = fs.readFileSync(helpPath, 'utf8');

// The alias notation must appear (e.g. "alias: `fr`")
expect(content).to.include('featurerequest');
expect(content).to.match(/featurerequest.*alias.*fr/i);
});
});
});
184 changes: 184 additions & 0 deletions test/slac-5-fr-alias.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/**
* Tests for SLAC-5 β€” Add "fr" Alias for `featurerequest` Command
*
* Verifies that:
* - "fr" is registered as an alias pointing to the same handler as "featurerequest"
* - The existing "featurerequest" command is unchanged
* - Help text documents the "fr" alias
* - No duplicated handler logic exists for "fr"
*/

import { describe, it } from 'mocha';
import { expect } from 'chai';
import fs from 'fs';
import path from 'path';

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function readFile(relativePath) {
return fs.readFileSync(path.join(process.cwd(), relativePath), 'utf8');
}

// ---------------------------------------------------------------------------
// Suite
// ---------------------------------------------------------------------------

describe('SLAC-5 β€” "fr" alias for featurerequest', function () {

// -------------------------------------------------------------------------
// Command Registration
// -------------------------------------------------------------------------

describe('Command Registration (index.js)', function () {

it('should still have the original "featurerequest" command registered', function () {
const content = readFile('index.js');
expect(content).to.include("['featurerequest', { fn: _featurerequest");
});

it('should have the "fr" alias registered', function () {
const content = readFile('index.js');
expect(content).to.include("['fr', { fn: _featurerequest");
});

it('should register "fr" with the same handler function as "featurerequest"', function () {
const content = readFile('index.js');

// Both entries must reference _featurerequest β€” no separate handler
const featureRequestRegistered = content.includes("['featurerequest', { fn: _featurerequest");
const frRegistered = content.includes("['fr', { fn: _featurerequest");

expect(featureRequestRegistered, '"featurerequest" registration with _featurerequest handler').to.be.true;
expect(frRegistered, '"fr" registration with _featurerequest handler').to.be.true;
Comment on lines +41 to +54
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test assumes aliases are registered as separate commandRegistry keys ("['fr', { fn: _featurerequest"), but the router uses an aliases array + aliasMap for alias resolution. As a result, this will fail until fr is added to featurerequest's aliases, and even then it will still be asserting the wrong shape. Adjust the test to validate alias registration via the aliases array / alias map instead of a duplicate registry entry.

Suggested change
it('should have the "fr" alias registered', function () {
const content = readFile('index.js');
expect(content).to.include("['fr', { fn: _featurerequest");
});
it('should register "fr" with the same handler function as "featurerequest"', function () {
const content = readFile('index.js');
// Both entries must reference _featurerequest β€” no separate handler
const featureRequestRegistered = content.includes("['featurerequest', { fn: _featurerequest");
const frRegistered = content.includes("['fr', { fn: _featurerequest");
expect(featureRequestRegistered, '"featurerequest" registration with _featurerequest handler').to.be.true;
expect(frRegistered, '"fr" registration with _featurerequest handler').to.be.true;
it('should register "fr" as an alias on the "featurerequest" command', function () {
const content = readFile('index.js');
const featureRequestEntryWithAlias = /\['featurerequest',\s*\{\s*fn:\s*_featurerequest[\s\S]*?aliases:\s*\[[^\]]*['"]fr['"][^\]]*\]/m;
expect(
featureRequestEntryWithAlias.test(content),
'"featurerequest" command should declare an aliases array containing "fr"'
).to.be.true;
});
it('should resolve aliases via alias metadata rather than a duplicate "fr" registry entry', function () {
const content = readFile('index.js');
const featureRequestRegistered = content.includes("['featurerequest', { fn: _featurerequest");
const duplicateFrRegistryEntry = content.includes("['fr', { fn: _featurerequest");
const hasAliasMapSupport = /aliasMap/.test(content) && /aliases/.test(content);
expect(featureRequestRegistered, '"featurerequest" registration with _featurerequest handler').to.be.true;
expect(duplicateFrRegistryEntry, '"fr" should not be registered as a separate commandRegistry entry').to.be.false;
expect(hasAliasMapSupport, 'index.js should use alias metadata / aliasMap for alias resolution').to.be.true;

Copilot uses AI. Check for mistakes.
});

it('should NOT define a separate handler function for "fr"', function () {
const content = readFile('index.js');

// There must be no dedicated _fr function β€” the alias reuses _featurerequest
expect(content).to.not.include('function _fr(');
expect(content).to.not.include('async function _fr(');
});

it('should have exactly one definition of the _featurerequest handler function', function () {
const content = readFile('index.js');

// Count occurrences of the function declaration
const matches = content.match(/async function _featurerequest\s*\(/g) || [];
expect(matches).to.have.lengthOf(1);
});

it('should have the _featurerequest handler accept (input, channel, userName) parameters', function () {
const content = readFile('index.js');
expect(content).to.include('async function _featurerequest(input, channel, userName)');
});
});

// -------------------------------------------------------------------------
// Handler Behaviour β€” source-level checks
// -------------------------------------------------------------------------

describe('Handler Behaviour', function () {

it('should use the GitHub Issues API endpoint in the handler', function () {
const content = readFile('index.js');
expect(content).to.include('api.github.com/repos/htilly/SlackONOS/issues');
});

it('should apply the "enhancement" label when creating a GitHub issue', function () {
const content = readFile('index.js');
expect(content).to.include("labels: ['enhancement']");
});

it('should include requester information in the issue body', function () {
const content = readFile('index.js');
expect(content).to.include('Requested by');
});
});

// -------------------------------------------------------------------------
Comment on lines +80 to +101
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests in this section are unrelated to adding the fr alias (they re-check GitHub API endpoint, labels, and issue body strings) and largely duplicate coverage already present in test/aicode-agent.test.mjs. Keeping both increases maintenance cost and makes tests brittle to harmless refactors; consider removing these checks from this SLAC-5-specific suite and focusing on alias behavior only.

Suggested change
// Handler Behaviour β€” source-level checks
// -------------------------------------------------------------------------
describe('Handler Behaviour', function () {
it('should use the GitHub Issues API endpoint in the handler', function () {
const content = readFile('index.js');
expect(content).to.include('api.github.com/repos/htilly/SlackONOS/issues');
});
it('should apply the "enhancement" label when creating a GitHub issue', function () {
const content = readFile('index.js');
expect(content).to.include("labels: ['enhancement']");
});
it('should include requester information in the issue body', function () {
const content = readFile('index.js');
expect(content).to.include('Requested by');
});
});
// -------------------------------------------------------------------------

Copilot uses AI. Check for mistakes.
// Help Text β€” user-facing documentation
// -------------------------------------------------------------------------

describe('Help Text Documentation', function () {

it('should document the "fr" alias in the standard help text', function () {
const content = readFile('templates/help/helpText.txt');
// The spec requires something like: featurerequest (alias: fr)
expect(content).to.include('featurerequest');
expect(content).to.include('fr');
});

it('should show "fr" as an alias for "featurerequest" in the standard help text', function () {
const content = readFile('templates/help/helpText.txt');
// Verify the alias notation is present (e.g. "alias: `fr`" or "(alias: fr)")
expect(content).to.match(/featurerequest.*alias.*fr/i);
});

it('should still document "featurerequest" in the admin help text', function () {
const content = readFile('templates/help/helpTextAdmin.txt');
expect(content).to.include('featurerequest');
});

it('should include the feature request entry in the Feedback section of standard help', function () {
const content = readFile('templates/help/helpText.txt');
// The Feedback section header must be present
expect(content).to.include('Feedback');
// And the featurerequest command must appear after it
const feedbackIndex = content.indexOf('Feedback');
const frIndex = content.indexOf('featurerequest', feedbackIndex);
expect(frIndex).to.be.greaterThan(feedbackIndex);
});
});

// -------------------------------------------------------------------------
// Configuration
// -------------------------------------------------------------------------

describe('Configuration', function () {

it('should have githubToken in the example config', function () {
const content = readFile('config/config.json.example');
expect(content).to.include('"githubToken"');
});
});

// -------------------------------------------------------------------------
// Alias parity β€” both keys must share identical registration shape
// -------------------------------------------------------------------------

describe('Alias Parity', function () {

it('"fr" and "featurerequest" registrations should reference the same handler name', function () {
const content = readFile('index.js');

// Extract the handler name used for each registration
const featureRequestMatch = content.match(/\['featurerequest',\s*\{\s*fn:\s*(\w+)/);
const frMatch = content.match(/\['fr',\s*\{\s*fn:\s*(\w+)/);

expect(featureRequestMatch, '"featurerequest" registration found').to.not.be.null;
expect(frMatch, '"fr" registration found').to.not.be.null;

const featureRequestHandler = featureRequestMatch[1];
const frHandler = frMatch[1];

expect(frHandler).to.equal(featureRequestHandler,
`"fr" handler (${frHandler}) must equal "featurerequest" handler (${featureRequestHandler})`);
});
Comment on lines +152 to +169
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acceptance criteria calls for verifying that invoking fr produces the same observable response/behavior as featurerequest (including on Slack and Discord). This suite only performs source-level string checks against index.js/templates, so it won’t catch runtime routing/response differences. Consider adding an executable test that runs the command router with fr vs featurerequest and asserts the same output is sent (with Slack/Discord message functions stubbed).

Copilot uses AI. Check for mistakes.

it('"fr" should not trigger an unknown-command response', function () {
const content = readFile('index.js');

// The command map must contain 'fr' so the router never falls through
// to an unknown-command branch for this input
expect(content).to.include("'fr'");

// Confirm the unknown-command guard (if present) would not match 'fr'
// by ensuring 'fr' is a registered key β€” already verified above
const frRegistered = content.includes("['fr', { fn: _featurerequest");
expect(frRegistered).to.be.true;
});
});
});
Loading