Skip to content

Commit 5f45bd8

Browse files
Sync public snapshot from freebuff-private
Source: CodebuffAI/freebuff-private@a88c66ae1d551cfaf7845dfd8ba53d70e0e2e275
1 parent 7632c29 commit 5f45bd8

4 files changed

Lines changed: 25 additions & 8 deletions

File tree

bun.lock

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/src/constants/__tests__/freebuff-free-promotions.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ describe('free-promotion spend windows', () => {
2323
from: new Date(Date.parse(free.at)).toISOString(),
2424
to: new Date(Date.parse(back.at)).toISOString(),
2525
},
26+
{
27+
modelId: FREEBUFF_SOLAR_PRO_4_MODEL_ID,
28+
from: '2026-09-09T15:49:00.000Z',
29+
to: '2099-01-01T00:00:00.000Z',
30+
},
2631
])
2732
})
2833

@@ -34,6 +39,8 @@ describe('free-promotion spend windows', () => {
3439
// The instant the price goes back to 5, spend counts again — with no
3540
// deploy, which is the point of deriving this from the schedule.
3641
expect(isFreePromotionSpendAt(solar, new Date('2026-09-08T00:00:00-07:00'))).toBe(false)
42+
expect(isFreePromotionSpendAt(solar, new Date('2026-09-09T15:48:59.999Z'))).toBe(false)
43+
expect(isFreePromotionSpendAt(solar, new Date('2026-09-09T15:49:00Z'))).toBe(true)
3744
})
3845

3946
it('exempts nothing else', () => {

common/src/constants/freebuff-solar-promo.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ export const SOLAR_REGULAR_OFFER = {
55
tagline: 'Limited-time trial',
66
} as const
77

8-
// The customer offer is September 5–7, Pacific time. These transitions travel
9-
// with the server quote so idle clients can update even during a slow refresh.
8+
// These transitions travel with the server quote so idle clients can update
9+
// even during a slow refresh. Preserve past prices for historical accounting.
1010
export const SOLAR_PRICE_CHANGES = [
1111
{
1212
at: '2026-09-05T00:00:00-07:00',
@@ -19,6 +19,12 @@ export const SOLAR_PRICE_CHANGES = [
1919
modelId: FREEBUFF_SOLAR_PRO_4_MODEL_ID,
2020
...SOLAR_REGULAR_OFFER,
2121
},
22+
{
23+
at: '2026-09-09T15:49:00Z',
24+
modelId: FREEBUFF_SOLAR_PRO_4_MODEL_ID,
25+
price: 0,
26+
tagline: '0 Freebucks',
27+
},
2228
] as const
2329

2430
export function solarOfferAt(now: number = Date.now()) {

common/src/util/__tests__/freebuff-price-changes.test.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ import {
1313
const solar = 'upstage/solar-pro4'
1414
const start = Date.parse('2026-09-05T07:00:00Z')
1515
const end = Date.parse('2026-09-08T07:00:00Z')
16+
const restored = Date.parse('2026-09-09T15:49:00Z')
1617
const quoteBeforeStart = () => ({
1718
...freebucksFixture(0, { [solar]: SOLAR_REGULAR_OFFER.price }),
1819
priceNotices: { [solar]: SOLAR_REGULAR_OFFER.tagline },
1920
priceChanges: [...SOLAR_PRICE_CHANGES],
2021
})
2122

2223
describe('announced Freebucks price changes', () => {
23-
it('keeps a serialized quote coherent at both boundaries without mutating balances or the input', () => {
24+
it('keeps a serialized quote coherent at every boundary without mutating balances or the input', () => {
2425
const quote = JSON.parse(JSON.stringify(quoteBeforeStart()))
2526
expect(applyFreebucksPriceChanges(quote, start - 1)).toBe(quote)
2627
const free = applyFreebucksPriceChanges(quote, start)
@@ -33,15 +34,20 @@ describe('announced Freebucks price changes', () => {
3334
expect(expired.balance).toBe(0)
3435
expect(expired.daily).toEqual(quote.daily)
3536
expect(expired.wallet).toEqual(quote.wallet)
36-
expect(nextFreebucksPriceChange(expired)).toBe(Infinity)
37+
expect(nextFreebucksPriceChange(expired)).toBe(restored)
38+
const freeAgain = applyFreebucksPriceChanges(expired, restored)
39+
expect(freeAgain.prices[solar]).toBe(0)
40+
expect(freeAgain.priceNotices[solar]).toBe('0 Freebucks')
41+
expect(nextFreebucksPriceChange(freeAgain)).toBe(Infinity)
3742
expect(quote.prices[solar]).toBe(5)
38-
expect(quote.priceChanges).toHaveLength(2)
43+
expect(quote.priceChanges).toHaveLength(3)
3944
})
4045

41-
it('catches up across both transitions, even when a delayed response lists them out of order', () => {
46+
it('catches up across all transitions, even when a delayed response lists them out of order', () => {
4247
const quote = quoteBeforeStart()
4348
quote.priceChanges.reverse()
4449
expect(applyFreebucksPriceChanges(quote, end).prices[solar]).toBe(5)
50+
expect(applyFreebucksPriceChanges(quote, restored).prices[solar]).toBe(0)
4551
})
4652

4753
it('does not add an unpriced model or invent metadata on older server responses', () => {

0 commit comments

Comments
 (0)