Skip to content

Commit

Permalink
Test RevenueCat webhook
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelbsky committed Dec 2, 2024
1 parent 601c296 commit 0d1f067
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 4 deletions.
1 change: 0 additions & 1 deletion packages/bsky/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import { authWithApiKey as bsyncAuth, createBsyncClient } from './bsync'
import { authWithApiKey as courierAuth, createCourierClient } from './courier'
import { FeatureGates } from './feature-gates'
import { VideoUriBuilder } from './views/util'
import axios from 'axios'
import { RevenueCatClient } from './revenueCat'

export * from './data-plane'
Expand Down
2 changes: 1 addition & 1 deletion packages/bsky/src/revenueCat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ type Config = {
webhookAuthorization: string | undefined
}

type GetSubscriberResponse = {
export type GetSubscriberResponse = {
subscriber: {
entitlements: {
[entitlementIdentifier: string]: unknown
Expand Down
136 changes: 136 additions & 0 deletions packages/bsky/tests/views/subscriptions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
import express from 'express'
import http from 'node:http'
import { once } from 'node:events'
import { GetSubscriberResponse } from '../../src/revenueCat'

describe('subscriptions views', () => {
let network: TestNetwork
let sc: SeedClient
let revenueCatServer: http.Server
let revenueCatHandler: jest.Mock
let bskyUrl: string

// account dids, for convenience
let alice: string

beforeAll(async () => {
const revenueCatPort = 48567

revenueCatHandler = jest.fn()
revenueCatServer = await createMockRevenueCatService(
revenueCatPort,
revenueCatHandler,
)

network = await TestNetwork.create({
dbPostgresSchema: 'bsky_views_subscriptions',
bsky: {
revenueCatV1ApiKey: 'any-key',
revenueCatV1Url: `http://localhost:${revenueCatPort}`,
},
})
bskyUrl = `http://localhost:${network.bsky.port}`
network.plc.getClient().updateData
sc = network.getSeedClient()
await basicSeed(sc)
await network.processAll()
alice = sc.dids.alice
})

afterAll(async () => {
await network.close()
revenueCatServer.close()
await once(revenueCatServer, 'close')
})

describe('webhook handler', () => {
it('sets the cache with the entitlements from the API response', async () => {
await network.bsky.db.db
.insertInto('subscription_entitlement')
.values({
did: alice,
entitlements: JSON.stringify(['entitlement0']),
})
.execute()

const before = await network.bsky.db.db
.selectFrom('subscription_entitlement')
.selectAll()
.execute()

expect(before).toStrictEqual([
{ did: alice, entitlements: ['entitlement0'] },
])

revenueCatHandler.mockImplementation((req, res) => {
const response: GetSubscriberResponse = {
subscriber: {
entitlements: {
entitlement1: {},
entitlement2: {},
},
},
}
res.json(response)
})

await fetch(`${bskyUrl}/webhooks/revenuecat`, {
method: 'POST',
body: JSON.stringify({ event: { app_user_id: alice } }),
headers: {
'Content-Type': 'application/json',
},
})

const after = await network.bsky.db.db
.selectFrom('subscription_entitlement')
.selectAll()
.execute()

expect(after).toStrictEqual([
{ did: alice, entitlements: ['entitlement1', 'entitlement2'] },
])
})

it('clears the cache if the API response returns no entitlements', async () => {
revenueCatHandler.mockImplementation((req, res) => {
const response: GetSubscriberResponse = {
subscriber: {
entitlements: {},
},
}
res.json(response)
})

await fetch(`${bskyUrl}/webhooks/revenuecat`, {
method: 'POST',
body: JSON.stringify({ event: { app_user_id: alice } }),
headers: {
'Content-Type': 'application/json',
},
})

const after = await network.bsky.db.db
.selectFrom('subscription_entitlement')
.selectAll()
.execute()

expect(after).toHaveLength(0)
})
})
})

async function createMockRevenueCatService(
port: number,
handler: jest.Mock,
): Promise<http.Server> {
const app = express()

app.use(express.json())
app.get('/subscribers/:did', handler)

const server = app.listen(port)
await once(server, 'listening')
return server
}
8 changes: 6 additions & 2 deletions packages/dev-env/src/bsky.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,13 @@ export class TestBsky {
...cfg,
adminPasswords: [ADMIN_PASSWORD],
revenueCatV1Url:
process.env.BSKY_REVENUE_CAT_V1_URL || 'https://api.revenuecat.com/v1',
revenueCatV1ApiKey: process.env.BSKY_REVENUE_CAT_V1_API_KEY,
cfg.revenueCatV1Url ||
process.env.BSKY_REVENUE_CAT_V1_URL ||
'https://api.revenuecat.com/v1',
revenueCatV1ApiKey:
cfg.revenueCatV1ApiKey || process.env.BSKY_REVENUE_CAT_V1_API_KEY,
revenueCatWebhookAuthorization:
cfg.revenueCatWebhookAuthorization ||
process.env.BSKY_REVENUE_CAT_WEBHOOK_AUTHORIZATION,
})

Expand Down

0 comments on commit 0d1f067

Please sign in to comment.