Skip to content

Conversation

@markstos
Copy link
Collaborator

@markstos markstos commented Sep 25, 2025

This started as a small, focused PR then snowballed as I worked on getting the tests to pass. Now what's included here includes:

  • Entire test suite uses mocks so it's easy to run locally or via CI. Expected responses are based on Strava's documented API.
  • Added Github workflow CI file to start testing all PRs.
  • Found that there four devDeps we no longer need: sinon, should env-restorer, mock-fs.
  • Found bugs in the test, code and docs along the way as the tests were brought back online. See individual commits for details.

After merging this, we should do a version bump and release.

Summary by CodeRabbit

  • Breaking Changes

    • Public API rename: strava.athletes.get() → strava.athlete.get()
    • Running Races feature and its list/get endpoints removed
    • Several endpoints marked deprecated (e.g., listFriends, listPhotos)
  • Documentation

    • API reference updated and test-running guidance expanded (mocked tests, tokens, error handling)
  • Chores

    • CI workflow added; devDependency and test tooling updates; test timeout reduced
  • Tests

    • Tests migrated to nock/assert and async/await with broader mocked coverage and per-test lifecycle hooks

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 25, 2025

Walkthrough

Documentation updated (strava.athlete.get); tests converted from callbacks/Should.js to async/await with Node assert and nock; RunningRaces runtime module, tests, and typings removed; axios/httpClient/streams behavior adjusted; package.json devDeps and mocha config changed; GitHub Actions workflow added.

Changes

Cohort / File(s) Summary
Docs
README.md
Updated public API examples to use strava.athlete.get, marked several endpoints deprecated, and revised test-running guidance (nock usage, integration token notes).
Package metadata
package.json
Added contributor Wesley Schlenker; removed multiple devDependencies (env-restorer, es6-promise, inquirer, mock-fs, should, sinon, yargs); updated devDependencies (mocha11.7.4, added tmp); adjusted mocha timeout 20000→5000 and removed globals entry.
Removed runtime module & typings
lib/runningRaces.js, test/runningRaces.js, index.js, index.d.ts
Deleted lib/runningRaces.js and its test; removed import/instantiation from index.js; removed RunningRacesRoutes type and runningRaces property from index.d.ts and public export.
Test refactor (many files)
test/*
test/_helper.js, test/activities.js, test/athlete.js, test/athletes.js, test/client.js, test/oauth.js, test/streams.js, test/uploads.js, test/segments.js, test/segmentEfforts.js, test/clubs.js, test/gear.js, test/routes.js, test/pushSubscriptions.js, test/rateLimiting.js, test/authenticator.js, test/config.js
Replaced Should/Sinon and callbacks with Node assert, async/await, and nock-based HTTP mocks; added per-test lifecycle hooks to setup/cleanup nock and auth state; removed FS-based mocks and legacy helpers (e.g., getAccessToken).
HTTP / networking utilities
axiosUtility.js, lib/httpClient.js
axiosUtility.js: replaced undefined validateStatus with explicit 2xx predicate when options.simple !== false. lib/httpClient.js: simplified string response handling — text returns raw, json/formdata parse entire string via JSONbig.parse; otherwise return string.
Library surface edits
lib/athlete.js, lib/athletes.js, lib/streams.js, lib/uploads.js
Removed athlete.prototype.listRoutes and athletes.prototype.get; streams: changed endpoint construction and added qs props (keys, key_by_type, original_size); uploads: minor formatting/whitespace edits.
CI workflow
.github/workflows/on-pull-request.yml
Added GitHub Actions workflow "Test Suite" for PRs to main (Node 20.x/22.x), installs deps with yarn and runs tests.
Scripts
scripts/generate-access-token.js
Replaced inquirer/yargs interactive flow with minimal arg parsing and readline prompts; simplified CLI flow to capture client credentials and exchange code for token.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Test as Mocha Test
  participant Client as Strava Client
  participant Nock as Nock (HTTP Mock)

  Note over Test,Nock: Tests use async/await and nock for HTTP interception

  Test->>Client: await strava.method(args)
  Client->>Nock: HTTP request (Authorization when expected)
  Nock-->>Client: mocked response (200/401/204/...)
  Client-->>Test: Promise resolves/rejects with parsed payload

  rect rgba(200,230,255,0.6)
    Note over Client: Upload flow (polled)
    Test->>Client: await strava.uploads.create(gpx)
    Client->>Nock: POST /api/v3/uploads
    Nock-->>Client: { id_str, status }
    loop poll until complete
      Client->>Nock: GET /api/v3/uploads/:id
      Nock-->>Client: status update (processing/completed)
    end
    Client-->>Test: final upload status
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Inspect and remove any remaining references to runningRaces across code, docs, and typings.
  • Review lib/httpClient.js JSONbig.parse change for callers expecting previous per-value reviver behavior.
  • Verify axiosUtility.validateStatus replacement covers intended status handling (simple vs non-simple).
  • Validate test nock interceptors and Authorization header expectations and CI mocha timeout compatibility.

Possibly related PRs

Poem

A nibble of code, a hop and a tweak,
Callbacks turned to promises, tidy and sleek.
Nock pads the endpoints, tokens snug in paw,
Running races folded — silence, then a draw.
CI lights blink — the rabbit hums, "All's law." 🐇

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: removing sinon as a development dependency because all mocks are now implemented through nock.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch drop-sinon

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between adbd33f and c005ba6.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (2)
  • package.json (2 hunks)
  • test/uploads.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/uploads.js
  • package.json

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 07b7d5c and c167b79.

📒 Files selected for processing (4)
  • README.md (2 hunks)
  • package.json (0 hunks)
  • test/_helper.js (0 hunks)
  • test/activities.js (3 hunks)
💤 Files with no reviewable changes (2)
  • package.json
  • test/_helper.js
🧰 Additional context used
🧬 Code graph analysis (1)
test/activities.js (3)
test/oauth.js (3)
  • nock (5-5)
  • strava (4-4)
  • payload (66-66)
index.js (2)
  • require (18-18)
  • strava (21-21)
lib/oauth.js (1)
  • require (2-2)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e58773a and a80118e.

📒 Files selected for processing (1)
  • test/client.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
test/client.js (2)
test/oauth.js (4)
  • should (1-1)
  • nock (5-5)
  • strava (4-4)
  • payload (66-66)
index.js (2)
  • require (18-18)
  • strava (21-21)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
test/activities.js (3)

8-11: Assert mocks are consumed and prevent accidental live network.

  • Add nock.isDone() verification to catch unconsumed interceptors.
  • Disable outbound network in this suite and restore afterward to guard against real calls.

Apply this diff:

   before(function () {
     // Set a default test activity to use in tests
     testActivity = { id: 123456789, resource_state: 3, name: 'Sample Activity' }
+    // Prevent accidental live network in this suite (restored in `after`)
+    nock.disableNetConnect()
   })
   
-  afterEach(function () {
-    // Clean all nock interceptors after each test
-    nock.cleanAll()
-  })
+  afterEach(function () {
+    // Assert all expected HTTP mocks were used
+    const done = nock.isDone()
+    const pending = done ? [] : nock.pendingMocks()
+    nock.cleanAll()
+    if (!done) {
+      throw new Error('Not all expected HTTP mocks were called: ' + pending.join(', '))
+    }
+  })
+
+  after(function () {
+    // Restore network behavior for other suites
+    nock.enableNetConnect()
+  })

Also applies to: 13-16


50-66: Avoid inter-test coupling on testActivity.id.

These tests depend on the prior create() test mutating testActivity. Prefer per-test known IDs or reset state in a beforeEach to make tests independent.


68-83: Duplicate of the previous get() test.

This repeats the same Promise flow and assertions. Consider removing to keep the suite lean.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a80118e and 398563b.

📒 Files selected for processing (1)
  • test/activities.js (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
test/activities.js (2)
test/client.js (4)
  • nock (3-3)
  • require (4-4)
  • strava (5-5)
  • should (2-2)
test/oauth.js (4)
  • nock (5-5)
  • strava (4-4)
  • payload (66-66)
  • should (1-1)
🔇 Additional comments (5)
test/activities.js (5)

2-2: Adopting nock for HTTP mocking looks good.

Import and usage are consistent with the new mocking approach.


85-100: Good verification of explicit token handling.

Precise header match ensures access_token passthrough is tested.


111-120: Update flow mock and assertions look correct.

Covers both resource_state and name.

Also applies to: 121-125


28-41: Nice end-to-end create() mock. Consider stabilizing token source.

Right now, most tests match any Bearer token. To make tests fully self-contained, either:

  • pass an explicit access_token in the call and match exactly in nock, or
  • seed STRAVA_ACCESS_TOKEN only when unset and restore it after (suite-level).

If you prefer the env approach, verify whether any suite already sets STRAVA_ACCESS_TOKEN. You can run:

#!/bin/bash
# Inspect tests for env token handling
rg -n -C2 -g '!**/node_modules/**' -P 'process\.env\.STRAVA_ACCESS_TOKEN' test

Also applies to: 42-47


170-178: LGTM on laps/comments/kudos mocks.

Clean, minimal responses with header checks; combined with the isDone() assertion, these will be reliable.

Also applies to: 179-183, 187-194, 195-199, 205-210, 211-215

@markstos
Copy link
Collaborator Author

I think Code Rabbit flagged a real bug here, which is that we have docs and a code comment that says we reject non-2xx responses with an error of type StatusCodeError, but it appears we don't actually do so... but it's not as simple a fix as just starting to do so, because we also have code about updating rate limits... which suggests in fact maybe there are some rate-limited HTTP response codes which we should maybe also treat as success instead of a status code error?

Also, now we have >50% of the test suite running without a live Strava API key, which means we could set up Github Actions to have it at at least run the tests that pass with mocks, then we can continue to work on mock'ing the API until we can run 100% of the test suite on mocks.

Mocks have their downsides, but the reality is that often now we don't run the tests at all due to the live API key requirement.

I think with Nock there may be a way that once we are completely mocked, there may a way we can enable people to disable all the mocks globally and use a real API key if they want to.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f186577 and 23235d3.

📒 Files selected for processing (1)
  • test/client.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
test/client.js (1)
test/oauth.js (4)
  • should (1-1)
  • nock (5-5)
  • strava (4-4)
  • payload (66-66)
🪛 Biome (2.1.2)
test/client.js

[error] 76-76: Expected a catch clause but instead found ')'.

Expected a catch clause here.

(parse)


[error] 78-78: expected a name for the function in a function declaration, but found none

(parse)

🔇 Additional comments (5)
test/client.js (5)

7-9: LGTM!

Explicit test tokens improve test isolation and eliminate file system dependencies.


12-15: LGTM!

The afterEach hook with nock.cleanAll() ensures proper cleanup of HTTP mocks between tests, preventing test interference.


20-35: LGTM!

The test correctly verifies that non-2xx responses are rejected with StatusCodeError. The should.fail() guard on Line 30 ensures the test cannot false-pass if the rejection doesn't occur.


37-56: LGTM!

The test correctly mocks a successful athlete data retrieval and validates the response payload structure.


78-99: LGTM once the syntax error is fixed.

This test correctly validates successful athlete data retrieval with a different mock response. It will function properly once the duplicate try statement on Line 67 is removed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
test/activities.js (2)

68-83: Remove redundant promise-based test.

Both #get() tests now use the same promise-based pattern after the Sinon-to-nock migration. The "(Promise API)" label is misleading since both tests are promise-based. Consider removing this duplicate test to reduce maintenance overhead.


19-215: Consider verifying nock interceptors were used.

While the tests correctly clean up nock with cleanAll(), they don't verify that all mocked requests were actually made. Calling scope.done() after each test assertion helps catch scenarios where expected HTTP calls never occurred.

Based on learnings

Example pattern for one test:

 it('should create an activity', function () {
   var args = {
     name: 'Most Epic Ride EVER!!!',
     elapsed_time: 18373,
     distance: 1557840,
     start_date_local: '2013-10-23T10:02:13Z',
     type: 'Ride'
   }

   // Mock the create activity API call
-  nock('https://www.strava.com')
+  const scope = nock('https://www.strava.com')
     .post('/api/v3/activities')
     .matchHeader('authorization', /Bearer .+/)
     .reply(201, {
       id: 987654321,
       resource_state: 3,
       name: 'Most Epic Ride EVER!!!',
       elapsed_time: 18373,
       distance: 1557840,
       start_date_local: '2013-10-23T10:02:13Z',
       type: 'Ride'
     })

   return strava.activities.create(args)
     .then(function (payload) {
       testActivity = payload;
       (payload.resource_state).should.be.exactly(3)
+      scope.done()
     })
 })
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 23235d3 and 177b329.

📒 Files selected for processing (1)
  • test/activities.js (3 hunks)
🔇 Additional comments (1)
test/activities.js (1)

8-16: LGTM! Proper test setup and cleanup.

The before hook initializes a concrete test activity, and the afterEach hook correctly cleans up nock interceptors between tests.

@markstos
Copy link
Collaborator Author

markstos commented Oct 9, 2025

@wesleyschlenker I get failures here for the athletes tests. Do they pass for you?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
test/client.js (5)

12-15: Consider strengthening mock isolation with network controls.

The afterEach cleanup is good, but tests could accidentally hit the real Strava API if nock mocks fail to match requests.

Add nock.disableNetConnect() in a beforeEach hook to ensure tests never make real network calls:

 describe('client_test', function () {
+  beforeEach(() => {
+    // Prevent any real network requests
+    nock.disableNetConnect()
+  })
+
   afterEach(() => {
     // Clean up after each test
     nock.cleanAll()
   })

Based on learnings: "Use nock.disableNetConnect() to ensure tests never make real network calls" and "Keep mocks isolated per-test and call nock.cleanAll() in test teardown."


20-35: Verify mock consumption to catch test logic errors.

The test correctly asserts error behavior, but doesn't verify the nock mock was actually consumed. If the mock fails to match, the test might hit unexpected code paths.

Capture the nock scope and verify it was consumed:

     it('Should reject promise with StatusCodeError for non-2xx response', async function () {
       // Mock athlete endpoint for BAD token -> 401
-      nock('https://www.strava.com')
+      const scope = nock('https://www.strava.com')
         .get('/api/v3/athlete')
         .matchHeader('authorization', 'Bearer ' + BAD_TOKEN)
         .reply(401, { message: 'Authorization Error', errors: [{ resource: 'Application', code: 'invalid' }] })

       const badClient = new strava.client(BAD_TOKEN)
       try {
         await badClient.athlete.get({})
         should.fail('Expected athlete.get to reject with StatusCodeError')
       } catch (err) {
         should(err).be.instanceOf(StatusCodeError)
         should(err.statusCode).equal(401)
       }
+
+      // Verify the mock was consumed
+      scope.done()
     })

Based on learnings: "Use scope.done() or nock.isDone() in teardown to assert expected requests were made."


37-56: Verify mock consumption for test correctness.

Same issue: without verifying the mock was consumed, the test could pass even if the mock wasn't used as expected.

Apply this pattern:

     it('should return detailed athlete information about athlete associated to access_token', function () {
       // Mock athlete endpoint for GOOD token -> 200
-      nock('https://www.strava.com')
+      const scope = nock('https://www.strava.com')
         .get('/api/v3/athlete')
         .matchHeader('authorization', 'Bearer ' + GOOD_TOKEN)
         .reply(200, {
           resource_state: 3,
           id: 12345,
           firstname: 'Test',
           lastname: 'User'
         })

       const client = new strava.client(GOOD_TOKEN)
       return client.athlete.get({})
         .then((payload) => {
           should(payload).be.ok()
           should(payload.resource_state).equal(3)
           should(payload.id).equal(12345)
+
+          // Verify the mock was consumed
+          scope.done()
         })
     })

58-75: Verify mock consumption.

     it('Should reject promise with StatusCodeError when using bad token', async function () {
       // Mock athlete endpoint for BAD token -> 401
       // Testing with a second interceptor to ensure nock works correctly
-      nock('https://www.strava.com')
+      const scope = nock('https://www.strava.com')
         .get('/api/v3/athlete')
         .matchHeader('authorization', 'Bearer ' + BAD_TOKEN)
         .reply(401, { message: 'Authorization Error' })

       const badClient = new strava.client(BAD_TOKEN)
       try {
         await badClient.athlete.get({})
         should.fail('Expected athlete.get to reject with StatusCodeError')
       } catch (err) {
         should(err).be.instanceOf(StatusCodeError)
         should(err.statusCode).equal(401)
         should(err.data.message).equal('Authorization Error')
       }
+
+      scope.done()
     })

77-98: Verify mock consumption.

     it('Should successfully return athlete data with valid token', function () {
       // Mock athlete endpoint for GOOD token -> 200
       // Testing a second successful request to verify client instances work correctly
-      nock('https://www.strava.com')
+      const scope = nock('https://www.strava.com')
         .get('/api/v3/athlete')
         .matchHeader('authorization', 'Bearer ' + GOOD_TOKEN)
         .reply(200, {
           resource_state: 3,
           id: 67890,
           firstname: 'Another',
           lastname: 'Athlete'
         })

       const client = new strava.client(GOOD_TOKEN)
       return client.athlete.get({})
         .then((payload) => {
           should(payload).be.ok()
           should(payload.resource_state).equal(3)
           should(payload.id).equal(67890)
           should(payload.firstname).equal('Another')
+
+          scope.done()
         })
     })
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 177b329 and 506194a.

📒 Files selected for processing (2)
  • test/_helper.js (1 hunks)
  • test/client.js (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-26T14:09:38.947Z
Learnt from: markstos
PR: node-strava/node-strava-v3#173
File: README.md:390-392
Timestamp: 2025-09-26T14:09:38.947Z
Learning: The node-strava-v3 repository contains a script at `scripts/generate-access-token.js` that helps users generate access tokens for testing purposes.

Applied to files:

  • test/_helper.js
🧬 Code graph analysis (1)
test/client.js (1)
test/oauth.js (4)
  • should (1-1)
  • nock (5-5)
  • strava (4-4)
  • payload (66-66)
🔇 Additional comments (3)
test/client.js (2)

2-3: LGTM! Proper imports for nock-based testing.

The addition of should and nock imports supports the migration to promise-based mocking.


7-9: LGTM! Explicit test tokens improve test clarity.

Using explicit token constants instead of file-based loading makes tests more isolated and easier to understand.

test/_helper.js (1)

1-1: LGTM! Clean migration from file-based token loading; no remaining getAccessToken() references in test files.

@markstos
Copy link
Collaborator Author

markstos commented Oct 9, 2025

The Code Rabbit "nitpick" feedback here looks like a good quality improvement.

@wesleyschlenker
Copy link
Collaborator

wesleyschlenker commented Oct 10, 2025

@markstos I get failures starting in the athlete_test, but it doesn't look like those tests use mock api calls yet. I'll migrate the rest of the tests to the mock api later when I have some time. Feel free to push back, but I think getting to 100% mocked first is a better strategy than starting on a fresh CI pipeline. Theoretically, CI isn't even needed if everyone is religiously running their tests, so I like the idea of tackling that problem first.

I hadn't realized you've assigned this PR to me, so I will try to carve out some time later.

@markstos
Copy link
Collaborator Author

The tests should pass on this branch are the ones that are modified and mocked, which are test/client.js and test/actitivites.js, but I currently get test failures in the client test. Looks like we are now throwing errors a different way that the test suite is not expecting.

 2) client_test
       #athlete.get()
         Should reject promise with StatusCodeError when using bad token:
     AssertionError: expected Error {
  operator: undefined,
  expected: undefined,
  stackStartFunction: Function { name: 'fail' },
  actual: 'Expected athlete.get to reject with StatusCodeError',
  negate: false,
  assertion: Assertion {
    obj: 'Expected athlete.get to reject with StatusCodeError',
    anyOne: false,
    negate: false,
    params: Object {
      operator: undefined,
      expected: undefined,
      message: undefined,
      stackStartFunction: Function { name: 'fail' },
      actual: 'Expected athlete.get to reject with StatusCodeError',
      negate: false,
      assertion: [Circular]
    }
  }
} to be an instance of StatusCodeError

markstos and others added 9 commits October 28, 2025 15:27
Also, remove dead test function for getting an access token.
- Added Wesley Schlenker to the list of contributors.
- Updated README to reflect the use of `yarn test` instead of `npm test` for running the test suite.
So no live Strava API key is required.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Mocks are based on sample API responses documented by Strava.

Now we can readily run tests locally and via CI.

- four devDeps are no longer needed:
   - sinon, env-restorer, should, mock-fs
- Add CI workflow to start running tests for every PR
- Process found several places where tests and code were out
  sync with current API, so tests, code or README were updated
  as needed.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
test/activities.js (1)

9-14: Implement token preservation in setupMockAuth and cleanupAuth.

The verification reveals that testHelper.setupMockAuth() does not implement the save/restore pattern for STRAVA_ACCESS_TOKEN. The current implementation directly sets hardcoded test values without saving the original token, and cleanupAuth() only calls authenticator.purge() without restoring it. This will clobber any existing token for integration tests.

Update test/_helper.js to:

  • Save the original STRAVA_ACCESS_TOKEN environment variable in setupMockAuth()
  • Restore it in cleanupAuth() after purging
🧹 Nitpick comments (3)
.github/workflows/on-pull-request.yml (1)

31-33: Consider removing continue-on-error once tests are stable.

While continue-on-error: true allows the workflow to proceed despite test failures, it prevents CI from catching regressions. Given the PR comments mention ongoing test failures, this may be intentional for incremental rollout, but it should be removed once the test suite is fully stabilized.

Would you like me to help track which tests are still failing so you can prioritize fixing them before removing this flag?

test/athletes.js (1)

7-17: Consider aligning setup/teardown pattern with other test files.

This file uses before/after for auth setup/cleanup, while other migrated test files (e.g., test/clubs.js, test/uploads.js, test/segments.js) use beforeEach/afterEach for both nock and auth. For consistency and to ensure test isolation, consider using beforeEach/afterEach here as well.

Apply this diff for consistency:

-  before(function () {
+  beforeEach(function () {
     testHelper.setupMockAuth()
   })

   afterEach(function () {
     nock.cleanAll()
+    testHelper.cleanupAuth()
   })

-  after(function () {
-    testHelper.cleanupAuth()
-  })
test/activities.js (1)

152-165: Skipped tests for premium features are appropriately marked.

The listZones and listPhotos tests remain skipped (xit) as they require premium accounts. This is fine for now, but consider adding mocked versions of these tests to verify the API client code paths work correctly, even if you can't test against the live API.

If you'd like, I can help generate mocked versions of these tests that would at least verify the client code handles the endpoints correctly.

Also applies to: 212-225

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c859fe3 and d5ca3b1.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (26)
  • .github/workflows/on-pull-request.yml (1 hunks)
  • README.md (3 hunks)
  • axiosUtility.js (1 hunks)
  • lib/athlete.js (0 hunks)
  • lib/athletes.js (0 hunks)
  • lib/httpClient.js (1 hunks)
  • lib/streams.js (2 hunks)
  • lib/uploads.js (1 hunks)
  • package.json (2 hunks)
  • test/_helper.js (2 hunks)
  • test/activities.js (4 hunks)
  • test/athlete.js (1 hunks)
  • test/athletes.js (1 hunks)
  • test/authenticator.js (1 hunks)
  • test/client.js (1 hunks)
  • test/clubs.js (1 hunks)
  • test/config.js (2 hunks)
  • test/gear.js (1 hunks)
  • test/oauth.js (3 hunks)
  • test/pushSubscriptions.js (3 hunks)
  • test/rateLimiting.js (3 hunks)
  • test/routes.js (1 hunks)
  • test/segmentEfforts.js (1 hunks)
  • test/segments.js (1 hunks)
  • test/streams.js (1 hunks)
  • test/uploads.js (1 hunks)
💤 Files with no reviewable changes (2)
  • lib/athlete.js
  • lib/athletes.js
✅ Files skipped from review due to trivial changes (1)
  • lib/uploads.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • README.md
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-26T14:09:38.947Z
Learnt from: markstos
Repo: node-strava/node-strava-v3 PR: 173
File: README.md:390-392
Timestamp: 2025-09-26T14:09:38.947Z
Learning: The node-strava-v3 repository contains a script at `scripts/generate-access-token.js` that helps users generate access tokens for testing purposes.

Applied to files:

  • test/oauth.js
  • test/client.js
  • test/authenticator.js
  • test/activities.js
  • test/_helper.js
  • test/config.js
📚 Learning: 2025-09-25T18:49:53.763Z
Learnt from: wesleyschlenker
Repo: node-strava/node-strava-v3 PR: 172
File: package.json:34-49
Timestamp: 2025-09-25T18:49:53.763Z
Learning: ESLint v8.57.1 can work with eslint-config-standard v12.0.0 in the node-strava-v3 project, despite theoretical version incompatibility warnings.

Applied to files:

  • package.json
🧬 Code graph analysis (10)
test/uploads.js (2)
lib/uploads.js (1)
  • require (1-1)
test/_helper.js (2)
  • strava (1-1)
  • payload (63-63)
test/athletes.js (1)
test/_helper.js (2)
  • strava (1-1)
  • payload (63-63)
test/streams.js (3)
test/activities.js (4)
  • assert (1-1)
  • strava (3-3)
  • nock (2-2)
  • testHelper (4-4)
test/routes.js (4)
  • assert (1-1)
  • strava (2-2)
  • nock (3-3)
  • testHelper (4-4)
test/_helper.js (1)
  • strava (1-1)
test/athlete.js (4)
test/activities.js (4)
  • assert (1-1)
  • strava (3-3)
  • nock (2-2)
  • testHelper (4-4)
test/athletes.js (4)
  • assert (1-1)
  • strava (2-2)
  • nock (3-3)
  • testHelper (4-4)
test/clubs.js (4)
  • assert (1-1)
  • strava (2-2)
  • nock (3-3)
  • testHelper (4-4)
test/_helper.js (1)
  • strava (1-1)
test/routes.js (1)
test/_helper.js (2)
  • strava (1-1)
  • payload (63-63)
lib/httpClient.js (1)
axiosUtility.js (1)
  • response (39-51)
test/rateLimiting.js (2)
index.js (2)
  • require (17-17)
  • rateLimiting (14-14)
lib/httpClient.js (1)
  • rateLimiting (5-5)
test/_helper.js (3)
test/config.js (1)
  • authenticator (3-3)
index.js (2)
  • require (17-17)
  • authenticator (3-3)
test/authenticator.js (1)
  • authenticator (2-2)
test/config.js (4)
test/activities.js (1)
  • assert (1-1)
test/authenticator.js (2)
  • assert (1-1)
  • authenticator (2-2)
test/oauth.js (2)
  • assert (1-1)
  • authenticator (2-2)
index.js (2)
  • require (17-17)
  • authenticator (3-3)
axiosUtility.js (2)
lib/httpClient.js (1)
  • options (40-45)
lib/oauth.js (3)
  • options (50-60)
  • options (71-81)
  • options (107-118)
🪛 GitHub Actions: Test Suite
lib/streams.js

[error] 10-10: ESLint: Unexpected trailing comma. (comma-dangle)

🪛 GitHub Check: test (20.x)
lib/streams.js

[failure] 10-10:
Unexpected trailing comma

🪛 GitHub Check: test (22.x)
lib/streams.js

[failure] 10-10:
Unexpected trailing comma

🪛 Gitleaks (8.28.0)
test/oauth.js

[high] 83-83: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 86-86: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 92-92: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 95-95: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (32)
test/_helper.js (1)

6-17: LGTM! Clean test helper API.

The new setupMockAuth() and cleanupAuth() methods provide a clean, explicit way to manage test authentication state, aligning well with the migration to nock-based mocking and async/await patterns.

test/config.js (1)

1-18: LGTM! Clean migration to assert.

The migration from Should.js to Node's built-in assert module is straightforward and maintains the same test semantics.

axiosUtility.js (1)

50-50: LGTM! Explicit 2xx validation aligns with StatusCodeError handling.

The explicit validateStatus predicate correctly enforces 2xx-only success when options.simple is not false, ensuring non-2xx responses trigger the error path that throws StatusCodeError (lines 54-61).

test/client.js (2)

7-15: LGTM! Clean test setup with explicit tokens and nock cleanup.

The explicit token constants and afterEach cleanup provide good test isolation, aligning well with the nock-based mocking approach.


20-37: LGTM! Proper error handling test with assert.fail guard.

The test correctly verifies that non-2xx responses result in a StatusCodeError, and the assert.fail() ensures the test won't silently pass if no error is thrown.

test/gear.js (2)

7-17: LGTM! Proper test lifecycle with mock auth setup and cleanup.

The before/after hooks correctly initialize and clean up mock authentication, while afterEach ensures nock interceptors are cleaned up between tests.


20-48: LGTM! Clean async test with nock-based mocking.

The test properly mocks the HTTP request with authorization header validation and uses assert.strictEqual for clear, explicit assertions.

test/rateLimiting.js (1)

2-78: LGTM! Thorough migration to assert with preserved test semantics.

The migration from Should.js to Node's assert module is comprehensive and maintains all test semantics. The explicit const headers declarations and assert.strictEqual comparisons make the tests more readable.

test/uploads.js (2)

7-16: LGTM: Proper test isolation setup.

The beforeEach/afterEach hooks correctly clean nock interceptors and manage mock authentication state, ensuring tests are properly isolated.


19-88: LGTM: Comprehensive upload flow test.

The test properly simulates the multi-step polling sequence with three distinct API responses (initial upload, first status check, second status check) and verifies both the callback invocation count and final payload structure.

test/pushSubscriptions.js (2)

7-10: LGTM: Proper nock cleanup.

The afterEach hook ensures interceptors are cleaned between tests, maintaining test isolation.


56-58: LGTM: Good defensive testing.

The tests properly validate that the API throws when required parameters are missing, helping catch client-side errors early.

Also applies to: 102-104

test/clubs.js (2)

7-16: LGTM: Consistent test isolation pattern.

The setup/teardown hooks follow the same pattern as other migrated test files, ensuring proper isolation and cleanup.


19-72: LGTM: Comprehensive club details test.

The test validates multiple fields including activity_types array, admin/owner flags, and membership status, providing good coverage of the API response structure.

test/athletes.js (1)

20-139: LGTM: Comprehensive stats validation.

The test thoroughly validates the structure of athlete stats, including all total categories (recent/ytd/all) and activity types (ride/run/swim), with both type checks and specific value assertions.

lib/streams.js (1)

42-44: LGTM: Cleaner query string handling.

The refactored endpoint construction uses getQS to build query parameters, which is more maintainable than the previous approach.

test/authenticator.js (2)

8-36: LGTM: Thorough environment management.

The setup/teardown properly saves, clears, and restores environment variables while also managing the authenticator cache, ensuring complete test isolation.


58-74: LGTM: Comprehensive console.log verification.

The test properly validates both the return value (undefined) and the logging behavior when the client ID is missing, ensuring users receive appropriate feedback.

test/segments.js (2)

7-15: LGTM: Consistent test isolation.

The setup/teardown pattern matches other migrated test files, ensuring proper cleanup and isolation.


179-219: LGTM: Thorough date range validation.

The test properly validates that returned efforts fall within the specified date range, ensuring the API filtering works correctly.

package.json (1)

48-48: No action required—timeout reduction is appropriate.

All HTTP-dependent tests use nock for mocking (client.js, uploads.js, streams.js). Tests without nock (rateLimiting.js, config.js, authenticator.js) test pure functions and configuration, requiring no network calls. The reduced 5-second timeout is safe for this fully-mocked test suite and aligns with the test infrastructure improvements mentioned in the PR.

test/routes.js (3)

1-17: LGTM: Clean test setup with proper isolation.

The test lifecycle hooks correctly establish mock authentication and clean up nock interceptors between tests, ensuring test isolation.


19-64: LGTM: Comprehensive route test with thorough assertions.

The test properly mocks the route endpoint with a complete data structure and validates both the response structure and data types.


66-146: LGTM: Export file tests validate XML structure appropriately.

Both GPX and TCX export tests properly mock the responses with correct Content-Type headers and validate the presence of expected XML elements.

test/oauth.js (1)

1-99: LGTM: OAuth tests properly migrated to async/await with nock.

The test suite comprehensively covers OAuth flows including deauthorization, token retrieval, and token refresh. The static analysis warnings about API keys on lines 83, 86, 92, and 95 are false positives—these are clearly mock tokens used only in test scenarios.

test/streams.js (1)

1-190: LGTM: Streams tests comprehensively validate data structures.

The test suite properly validates all stream types (activity, effort, segment, route) with appropriate assertions for array structure and data presence. The use of find and map to validate specific stream types is a good pattern.

test/segmentEfforts.js (1)

1-90: LGTM: Detailed segment effort test with comprehensive validation.

The test properly validates nested data structures including segment details and athlete stats. The use of before/after instead of beforeEach/afterEach is appropriate since there's only one test in this suite.

test/athlete.js (1)

1-384: LGTM: Comprehensive athlete endpoint coverage with detailed mocks.

The test suite thoroughly covers all athlete-related endpoints with realistic, detailed mock data. The listActivities test is particularly thorough with complete activity payloads. Good use of both simple field assertions and array structure validations.

test/activities.js (4)

21-50: LGTM: Activity creation test properly mocked.

The test correctly mocks the POST endpoint and validates the response structure.


52-100: LGTM: Get activity tests cover multiple scenarios.

Good coverage of the standard flow, Promise API, and explicit access token usage. All three variants properly validate authorization headers.


102-150: LGTM: Update tests validate both name and sport_type changes.

Both update tests properly mock the PUT endpoint and validate the response fields match the updates.


167-210: LGTM: List operations properly test array responses.

The tests for listLaps, listComments, and listKudos correctly mock their respective endpoints and validate array responses.

@node-strava node-strava deleted a comment from github-actions bot Nov 3, 2025
@node-strava node-strava deleted a comment from github-actions bot Nov 3, 2025
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@node-strava node-strava deleted a comment from github-actions bot Nov 3, 2025
@github-actions
Copy link

github-actions bot commented Nov 3, 2025

Test Results (Node 20.x)

0 tests   0 ✅  0s ⏱️
0 suites  0 💤
1 files    0 ❌
1 errors

For more details on these parsing errors, see this check.

Results for commit 4cfb1da.

@node-strava node-strava deleted a comment from github-actions bot Nov 3, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
scripts/generate-access-token.js (2)

60-60: Use const instead of var for variables that are not reassigned.

Lines 60, 66, and 78 declare variables with var that are never reassigned. Modern JavaScript style prefers const for immutability.

Apply this diff:

- var content = fs.readFileSync(stravaConfigTemplate)
+ const content = fs.readFileSync(stravaConfigTemplate)
- var config = JSON.parse(content)
+ const config = JSON.parse(content)
- var url = strava.oauth.getRequestAccessURL({
+ const url = strava.oauth.getRequestAccessURL({

Also applies to: 66-66, 78-78


88-89: rl.close() before process.exit() is redundant.

process.exit() terminates the process immediately, so the preceding rl.close() may not complete. Since process.exit() cleans up resources, the explicit close is unnecessary here.

Apply this diff:

  if (!code) {
    console.log('no code provided')
-   rl.close()
    process.exit()
  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a95ed10 and 4ff744c.

📒 Files selected for processing (2)
  • package.json (2 hunks)
  • scripts/generate-access-token.js (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-26T14:09:38.947Z
Learnt from: markstos
Repo: node-strava/node-strava-v3 PR: 173
File: README.md:390-392
Timestamp: 2025-09-26T14:09:38.947Z
Learning: The node-strava-v3 repository contains a script at `scripts/generate-access-token.js` that helps users generate access tokens for testing purposes.

Applied to files:

  • scripts/generate-access-token.js
📚 Learning: 2025-09-25T18:49:53.763Z
Learnt from: wesleyschlenker
Repo: node-strava/node-strava-v3 PR: 172
File: package.json:34-49
Timestamp: 2025-09-25T18:49:53.763Z
Learning: ESLint v8.57.1 can work with eslint-config-standard v12.0.0 in the node-strava-v3 project, despite theoretical version incompatibility warnings.

Applied to files:

  • package.json
🧬 Code graph analysis (1)
scripts/generate-access-token.js (1)
test/oauth.js (1)
  • url (29-31)
🔇 Additional comments (4)
scripts/generate-access-token.js (1)

92-104: LGTM: Error handling is well-structured.

The try-catch blocks properly handle errors during token exchange, and the top-level catch ensures rl.close() is called before exit. Good defensive coding.

Also applies to: 106-110

package.json (3)

22-23: LGTM: Contributor list updated appropriately.


43-43: Verify nock version compatibility before upgrading.

The latest nock version is 14.0.10, while the code uses 11.9.1. This represents a 2+ major version jump. Before upgrading, verify:

  • Whether breaking changes exist between versions
  • Whether the older version was intentionally pinned for compatibility
  • Whether code changes are required for the newer version

If upgrading, test thoroughly to ensure mock behavior remains consistent.


46-46: Timeout reduction is appropriate; all tests use mocks.

Code review confirms the 5000ms timeout is suitable. The test suite has been successfully migrated to mocked API calls via nock—all 18 test files use .reply() patterns for HTTP mocking. The unmocked helper functions in test/_helper.js (getSampleActivity, getSampleClub, etc.) are unused legacy code and won't execute. Mocked tests complete well within 5 seconds, making the timeout reduction from 20000ms safe and aligned with the faster test execution.

Copy link
Collaborator

@wesleyschlenker wesleyschlenker left a comment

Choose a reason for hiding this comment

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

Looks good, works well. The release notes are going to be fun lol. I also noticed some eslint errors. Now that we have CI up and running, we should lint and add it to the workflow.

@wesleyschlenker wesleyschlenker merged commit c1c784f into main Nov 7, 2025
3 checks passed
@wesleyschlenker wesleyschlenker linked an issue Nov 7, 2025 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Drop GET /athletes support

3 participants