BREAKING CHANGE: the marketplace slug, the agent namespace (linkedin-studio:<agent>), and the runtime state-file path (~/.claude/linkedin-studio.local.md) all change. Reinstall required; existing state migrated in place (post metrics, streak, history preserved). The /linkedin:* commands are unchanged — the command namespace is set per-command in frontmatter and was always independent of the plugin slug. Functionality is byte-identical to v2.4.0; this release is pure identity. - dir + manifests: plugins/linkedin-studio + plugin.json + root marketplace.json - agent namespace updated in commands/newsletter.md (only functional invoker) - state path updated in 4 hook scripts + topic-rotation prompt + state template - catch-all skill dir renamed skills/linkedin-studio (5 functional skills unchanged) - docs + version bump to 3.0.0 across README badge, CHANGELOG, root README/CLAUDE.md - historical records (CHANGELOG past entries, docs/ build artifacts, config-audit v5.0.0 snapshots) intentionally retain the old slug Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
139 lines
4 KiB
TypeScript
139 lines
4 KiB
TypeScript
import { describe, test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import {
|
|
mean,
|
|
standardDeviation,
|
|
trendDirection,
|
|
percentChange,
|
|
deviationsFromMean,
|
|
} from "../src/utils/stats.js";
|
|
|
|
describe("stats", () => {
|
|
describe("mean", () => {
|
|
test("should return mean of values", () => {
|
|
const result = mean([10, 20, 30]);
|
|
assert.equal(result, 20);
|
|
});
|
|
|
|
test("should return 0 for empty array", () => {
|
|
const result = mean([]);
|
|
assert.equal(result, 0);
|
|
});
|
|
|
|
test("should handle single value", () => {
|
|
const result = mean([42]);
|
|
assert.equal(result, 42);
|
|
});
|
|
});
|
|
|
|
describe("standardDeviation", () => {
|
|
test("should calculate correctly for known values", () => {
|
|
// For [2, 4, 4, 4, 5, 5, 7, 9]:
|
|
// Mean = 5
|
|
// Variance = ((2-5)^2 + (4-5)^2 + (4-5)^2 + (4-5)^2 + (5-5)^2 + (5-5)^2 + (7-5)^2 + (9-5)^2) / 8
|
|
// Variance = (9 + 1 + 1 + 1 + 0 + 0 + 4 + 16) / 8 = 32 / 8 = 4
|
|
// StdDev = 2
|
|
const result = standardDeviation([2, 4, 4, 4, 5, 5, 7, 9]);
|
|
assert.equal(result, 2);
|
|
});
|
|
|
|
test("should return 0 for single value", () => {
|
|
const result = standardDeviation([5]);
|
|
assert.equal(result, 0);
|
|
});
|
|
|
|
test("should return 0 for empty array", () => {
|
|
const result = standardDeviation([]);
|
|
assert.equal(result, 0);
|
|
});
|
|
|
|
test("should handle uniform values", () => {
|
|
const result = standardDeviation([5, 5, 5, 5]);
|
|
assert.equal(result, 0);
|
|
});
|
|
});
|
|
|
|
describe("trendDirection", () => {
|
|
test("should detect up trend", () => {
|
|
const result = trendDirection(110, 100);
|
|
assert.equal(result, "up");
|
|
});
|
|
|
|
test("should detect down trend", () => {
|
|
const result = trendDirection(90, 100);
|
|
assert.equal(result, "down");
|
|
});
|
|
|
|
test("should detect stable trend", () => {
|
|
const result = trendDirection(103, 100);
|
|
assert.equal(result, "stable");
|
|
});
|
|
|
|
test("should use custom threshold", () => {
|
|
const result = trendDirection(103, 100, 10);
|
|
assert.equal(result, "stable");
|
|
});
|
|
|
|
test("should detect up with custom threshold", () => {
|
|
const result = trendDirection(112, 100, 10);
|
|
assert.equal(result, "up");
|
|
});
|
|
});
|
|
|
|
describe("percentChange", () => {
|
|
test("should calculate positive change correctly", () => {
|
|
const result = percentChange(110, 100);
|
|
assert.equal(result, 10);
|
|
});
|
|
|
|
test("should calculate negative change correctly", () => {
|
|
const result = percentChange(90, 100);
|
|
assert.equal(result, -10);
|
|
});
|
|
|
|
test("should handle zero previous value", () => {
|
|
const result = percentChange(100, 0);
|
|
assert.equal(result, 0);
|
|
});
|
|
|
|
test("should handle zero current value", () => {
|
|
const result = percentChange(0, 100);
|
|
assert.equal(result, -100);
|
|
});
|
|
|
|
test("should handle no change", () => {
|
|
const result = percentChange(100, 100);
|
|
assert.equal(result, 0);
|
|
});
|
|
});
|
|
|
|
describe("deviationsFromMean", () => {
|
|
test("should calculate correctly for value above mean", () => {
|
|
// Mean of [10, 20, 30] = 20
|
|
// StdDev = sqrt(((10-20)^2 + (20-20)^2 + (30-20)^2) / 3) = sqrt((100 + 0 + 100) / 3) = sqrt(66.67) ≈ 8.165
|
|
// Deviations for 30 = (30 - 20) / 8.165 ≈ 1.225
|
|
const result = deviationsFromMean(30, [10, 20, 30]);
|
|
assert.ok(Math.abs(result - 1.225) < 0.01);
|
|
});
|
|
|
|
test("should calculate correctly for value below mean", () => {
|
|
const result = deviationsFromMean(10, [10, 20, 30]);
|
|
assert.ok(Math.abs(result + 1.225) < 0.01); // Negative deviation
|
|
});
|
|
|
|
test("should return 0 for uniform data", () => {
|
|
const result = deviationsFromMean(5, [5, 5, 5]);
|
|
assert.equal(result, 0);
|
|
});
|
|
|
|
test("should return 0 for single value", () => {
|
|
const result = deviationsFromMean(5, [5]);
|
|
assert.equal(result, 0);
|
|
});
|
|
|
|
test("should calculate for value at mean", () => {
|
|
const result = deviationsFromMean(20, [10, 20, 30]);
|
|
assert.ok(Math.abs(result) < 0.01);
|
|
});
|
|
});
|
|
});
|