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
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,41 @@
# test-scratch-repo

Scratch repo for testing Pullfrog triggers.

## API

```js
const { add, subtract, average, median } = require("./index");
```

### `add(a, b)`

Returns the sum of `a` and `b`.

```js
add(1, 2); // 3
```

### `subtract(a, b)`

Returns `a` minus `b`.

```js
subtract(5, 3); // 2
```

### `average(numbers)`

Returns the arithmetic mean of an array of numbers. Returns `0` for an empty array and throws a `TypeError` if `numbers` is not an array of numbers.

```js
average([1, 2, 3, 4]); // 2.5
```

### `median(numbers)`

Returns the middle value of an array of numbers, or the mean of the two middle values when the length is even. The input array is not mutated. Returns `0` for an empty array and throws a `TypeError` if `numbers` is not an array of numbers.

```js
median([4, 1, 3, 2]); // 2.5
```
21 changes: 20 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,23 @@ function subtract(a, b) {
return a - b;
}

module.exports = { add, subtract };
function average(numbers) {
if (!Array.isArray(numbers) || !numbers.every((n) => typeof n === "number")) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sparse arrays get past this check, because Array.prototype.every skips holes. average([1, , 3]) returns 1.333…: reduce also skips the hole, but the code still divides by length 3. median([1, , 3]) returns 3, because spreading turns the hole into undefined and sort moves it to the end. NaN also passes typeof n === "number", and then both helpers return NaN. The same check is copied at line 19, so whatever fix you choose belongs in both places, ideally in one shared validator.

Technical details
# Sparse arrays and NaN bypass input validation

## Affected sites
- index.js:10 — `average` validation uses `numbers.every(...)`, which skips holes; `typeof` accepts `NaN`
- index.js:19 — identical check in `median`

## Required outcome
- `average([1, , 3])` and `median([1, , 3])` throw `TypeError` (or otherwise don't silently return a wrong value)
- Decide whether `NaN` should be rejected; if so, both helpers throw `TypeError` for it
- Add matching `assert.throws` cases in test.js

## Suggested approach
- Extract one shared `assertNumberArray(numbers, name)` helper. Check every index with a plain `for` loop (or `Array.from(numbers).every(...)`) so holes come through as `undefined`, and use `Number.isFinite` / `!Number.isNaN` if `NaN` should be rejected.

throw new TypeError("average expects an array of numbers");
}
if (numbers.length === 0) return 0;
const total = numbers.reduce((sum, n) => sum + n, 0);
return total / numbers.length;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

For an empty array this computes 0 / 0, which is NaN. #8 says an empty array should return 0. Running node test.js on this branch fails at test.js:8 with AssertionError: NaN !== 0.

Suggested change
return total / numbers.length;
if (numbers.length === 0) return 0;
return total / numbers.length;

}

function median(numbers) {
if (!Array.isArray(numbers) || !numbers.every((n) => typeof n === "number")) {
throw new TypeError("median expects an array of numbers");
}
if (numbers.length === 0) return 0;
const sorted = [...numbers].sort((a, b) => a - b);
const mid = Math.floor(sorted.length / 2);
return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
}

module.exports = { add, subtract, average, median };
25 changes: 24 additions & 1 deletion test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
const { add, subtract } = require("./index");
const { add, subtract, average, median } = require("./index");

const assert = require("assert");

assert.strictEqual(add(1, 2), 3);
assert.strictEqual(subtract(5, 3), 2);
assert.strictEqual(average([1, 2, 3, 4]), 2.5);
assert.strictEqual(average([]), 0);
assert.throws(() => average(), TypeError);
assert.throws(() => average(null), TypeError);
assert.throws(() => average("123"), TypeError);
assert.throws(() => average({ length: 2 }), TypeError);
assert.throws(() => average([1, "2", 3]), TypeError);
assert.throws(() => average([1, null]), TypeError);
assert.strictEqual(median([3, 1, 2]), 2);
assert.strictEqual(median([4, 1, 3, 2]), 2.5);
assert.strictEqual(median([10, 9, 100]), 10);
assert.strictEqual(median([]), 0);
const unsorted = [3, 1, 2];
median(unsorted);
assert.deepStrictEqual(unsorted, [3, 1, 2]);
assert.throws(() => median(), TypeError);
assert.throws(() => median(null), TypeError);
assert.throws(() => median("123"), TypeError);
assert.throws(() => median({ length: 2 }), TypeError);
assert.throws(() => median([1, "2", 3]), TypeError);
assert.throws(() => median([1, null]), TypeError);

assert.strictEqual(average([5]), 5);

console.log("All tests passed!");
Loading