Skip to content
Open
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
4 changes: 3 additions & 1 deletion src/user_management.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ function get_account_info_payload(subscriber_number, account, authenticated = fa
// We consider one year to be 360 days, to be a bit lenient with users who might have a few days of downtime in their subscription, and make sure everyone who roughly got a year of service gets the benefit during the announcement.
const one_year_in_seconds = 360 * 24 * 60 * 60
// Performance optimization: We only calculate the total membership time if the account is active
const member_for_more_than_one_year = account_active ? total_active_membership_time(account) > one_year_in_seconds : false
const total_membership_time = account_active ? total_active_membership_time(account) : 0
const member_for_more_than_one_year = total_membership_time > one_year_in_seconds


return {
Expand All @@ -192,6 +193,7 @@ function get_account_info_payload(subscriber_number, account, authenticated = fa
testflight_url: (authenticated && account_active) ? process.env.TESTFLIGHT_URL : null,
attributes: {
member_for_more_than_one_year: member_for_more_than_one_year,
active_membership_duration: total_membership_time,
}
}
}
Expand Down
117 changes: 105 additions & 12 deletions test/router_config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const express = require('express');
const config_router = require('../src/router_config.js').config_router;
const nostr = require('nostr');
const current_time = require('../src/utils.js').current_time;
const { get_account_info_payload } = require('../src/user_management.js');
const { supertest_client } = require('./controllers/utils.js');
const { v4: uuidv4 } = require('uuid')

Expand Down Expand Up @@ -84,20 +85,112 @@ test('config_router - Account management routes', async (t) => {
.get('/accounts/abc123')
.expect(200);

const expectedData = {
pubkey: account_info.pubkey,
created_at: account_info.created_at,
subscriber_number: 1,
expiry: account_info.expiry,
active: true,
testflight_url: null,
attributes: {
member_for_more_than_one_year: false
}
};
t.same(res.body, expectedData, 'Response should match expected value');
t.equal(res.body.pubkey, account_info.pubkey)
t.equal(res.body.created_at, account_info.created_at)
t.equal(res.body.subscriber_number, 1)
t.equal(res.body.expiry, account_info.expiry)
t.equal(res.body.active, true)
t.equal(res.body.testflight_url, null)
t.equal(res.body.attributes.member_for_more_than_one_year, false)
// Legacy account with 30-day past + 30-day future expiry yields ~60 days of membership
const sixty_days = 60 * 24 * 60 * 60
t.ok(res.body.attributes.active_membership_duration > sixty_days - 10, 'duration should be approximately 60 days')
t.ok(res.body.attributes.active_membership_duration < sixty_days + 10, 'duration should be approximately 60 days')
Comment on lines +88 to +98
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing member_for_more_than_three_years assertion in route test

The route test validates member_for_more_than_one_year but never asserts on the new member_for_more_than_three_years field. Since the account is only ~30 days old, the expected value is false.

🐛 Proposed fix
  t.equal(res.body.attributes.member_for_more_than_one_year, false)
+ t.equal(res.body.attributes.member_for_more_than_three_years, false)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
t.equal(res.body.pubkey, account_info.pubkey)
t.equal(res.body.created_at, account_info.created_at)
t.equal(res.body.subscriber_number, 1)
t.equal(res.body.expiry, account_info.expiry)
t.equal(res.body.active, true)
t.equal(res.body.testflight_url, null)
t.equal(res.body.attributes.member_for_more_than_one_year, false)
// Legacy account with 30-day past + 30-day future expiry yields ~60 days of membership
const sixty_days = 60 * 24 * 60 * 60
t.ok(res.body.attributes.active_membership_duration > sixty_days - 10, 'duration should be approximately 60 days')
t.ok(res.body.attributes.active_membership_duration < sixty_days + 10, 'duration should be approximately 60 days')
t.equal(res.body.pubkey, account_info.pubkey)
t.equal(res.body.created_at, account_info.created_at)
t.equal(res.body.subscriber_number, 1)
t.equal(res.body.expiry, account_info.expiry)
t.equal(res.body.active, true)
t.equal(res.body.testflight_url, null)
t.equal(res.body.attributes.member_for_more_than_one_year, false)
t.equal(res.body.attributes.member_for_more_than_three_years, false)
// Legacy account with 30-day past + 30-day future expiry yields ~60 days of membership
const sixty_days = 60 * 24 * 60 * 60
t.ok(res.body.attributes.active_membership_duration > sixty_days - 10, 'duration should be approximately 60 days')
t.ok(res.body.attributes.active_membership_duration < sixty_days + 10, 'duration should be approximately 60 days')
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/router_config.test.js` around lines 88 - 98, The test for the account
route is missing an assertion for the new
attributes.member_for_more_than_three_years field; update the test in
router_config.test.js (the block using t.equal and res.body.attributes) to
assert that res.body.attributes.member_for_more_than_three_years is false for
this ~30-day legacy account, placing the assertion alongside the existing
member_for_more_than_one_year check so the test verifies both flags.

t.end();
});

t.end();
});

test('get_account_info_payload - membership tenure attributes', async (t) => {
const one_year_in_seconds = 360 * 24 * 60 * 60
const thirty_days_in_seconds = 60 * 60 * 24 * 30

t.test('new account returns duration and member_for_more_than_one_year false', async (t) => {
const account = {
pubkey: 'abc123',
created_at: current_time() - thirty_days_in_seconds,
expiry: current_time() + thirty_days_in_seconds,
transactions: [{
type: 'iap',
id: '1',
start_date: current_time() - thirty_days_in_seconds,
end_date: current_time() + thirty_days_in_seconds,
purchased_date: current_time() - thirty_days_in_seconds,
duration: null
}]
}
const payload = get_account_info_payload(1, account)
t.equal(payload.attributes.member_for_more_than_one_year, false)
t.ok(payload.attributes.active_membership_duration > 0, 'duration should be positive for active account')
t.ok(payload.attributes.active_membership_duration < one_year_in_seconds, 'duration should be less than one year')
t.end()
})

t.test('account with > 3 years returns correct duration', async (t) => {
const total_duration = 3 * one_year_in_seconds + 1
const account = {
pubkey: 'abc123',
created_at: current_time() - total_duration,
expiry: current_time() + thirty_days_in_seconds,
transactions: [{
type: 'iap',
id: '1',
start_date: current_time() - total_duration,
end_date: current_time(),
purchased_date: current_time() - total_duration,
duration: null
}]
}
const payload = get_account_info_payload(1, account)
t.equal(payload.attributes.member_for_more_than_one_year, true)
t.ok(payload.attributes.active_membership_duration > 3 * one_year_in_seconds, 'duration should exceed three years')
t.end()
})

t.test('account with > 1 year but < 3 years returns correct duration', async (t) => {
const total_duration = one_year_in_seconds + 1
const account = {
pubkey: 'abc123',
created_at: current_time() - total_duration,
expiry: current_time() + thirty_days_in_seconds,
transactions: [{
type: 'iap',
id: '1',
start_date: current_time() - total_duration,
end_date: current_time(),
purchased_date: current_time() - total_duration,
duration: null
}]
}
const payload = get_account_info_payload(1, account)
t.equal(payload.attributes.member_for_more_than_one_year, true)
t.ok(payload.attributes.active_membership_duration > one_year_in_seconds)
t.ok(payload.attributes.active_membership_duration < 3 * one_year_in_seconds)
t.end()
})

t.test('inactive account returns zero duration', async (t) => {
const total_duration = 3 * one_year_in_seconds + 1
const account = {
pubkey: 'abc123',
created_at: current_time() - total_duration,
expiry: current_time() - 1, // expired
transactions: [{
type: 'iap',
id: '1',
start_date: current_time() - total_duration,
end_date: current_time() - 1,
purchased_date: current_time() - total_duration,
duration: null
}]
}
const payload = get_account_info_payload(1, account)
t.equal(payload.active, false)
t.equal(payload.attributes.member_for_more_than_one_year, false)
t.equal(payload.attributes.active_membership_duration, 0)
t.end()
})
Comment on lines +109 to +193
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

New test suite never asserts on member_for_more_than_three_years — the primary attribute added by this PR

All four sub-tests check member_for_more_than_one_year and active_membership_duration but completely omit the member_for_more_than_three_years attribute. This contradicts the stated test plan ("Verify accounts with > 1080 days return member_for_more_than_three_years: true", "Verify inactive accounts return false for both attributes").

Expected assertions per sub-test:

Test case member_for_more_than_three_years
New account (30 days) false
> 3 years true
> 1 year but < 3 years false
Inactive false
🐛 Proposed additions
  // new account test (~line 124)
  t.equal(payload.attributes.member_for_more_than_one_year, false)
+ t.equal(payload.attributes.member_for_more_than_three_years, false)

  // > 3 years test (~line 146)
  t.equal(payload.attributes.member_for_more_than_one_year, true)
+ t.equal(payload.attributes.member_for_more_than_three_years, true)

  // > 1 year but < 3 years test (~line 167)
  t.equal(payload.attributes.member_for_more_than_one_year, true)
+ t.equal(payload.attributes.member_for_more_than_three_years, false)

  // inactive account test (~line 190)
  t.equal(payload.attributes.member_for_more_than_one_year, false)
+ t.equal(payload.attributes.member_for_more_than_three_years, false)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/router_config.test.js` around lines 109 - 193, Add assertions for the
new member_for_more_than_three_years attribute in each sub-test that calls
get_account_info_payload: for the "new account" test assert
payload.attributes.member_for_more_than_three_years === false; for the "> 3
years" test assert payload.attributes.member_for_more_than_three_years === true;
for the "> 1 year but < 3 years" test assert
payload.attributes.member_for_more_than_three_years === false; and for the
"inactive account" test assert
payload.attributes.member_for_more_than_three_years === false (also ensure the
inactive test still checks payload.active and active_membership_duration as
before).


t.end()
});