@linkforty/core 1.44.1 → 1.44.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -588
- package/llms.txt +1 -1
- package/package.json +2 -1
- package/dist/index.config.test.d.ts +0 -2
- package/dist/index.config.test.d.ts.map +0 -1
- package/dist/index.config.test.js +0 -87
- package/dist/index.config.test.js.map +0 -1
- package/dist/lib/bot-detection.test.d.ts +0 -2
- package/dist/lib/bot-detection.test.d.ts.map +0 -1
- package/dist/lib/bot-detection.test.js +0 -65
- package/dist/lib/bot-detection.test.js.map +0 -1
- package/dist/lib/client-ip.test.d.ts +0 -7
- package/dist/lib/client-ip.test.d.ts.map +0 -1
- package/dist/lib/client-ip.test.js +0 -114
- package/dist/lib/client-ip.test.js.map +0 -1
- package/dist/lib/config.test.d.ts +0 -2
- package/dist/lib/config.test.d.ts.map +0 -1
- package/dist/lib/config.test.js +0 -126
- package/dist/lib/config.test.js.map +0 -1
- package/dist/lib/database.test.d.ts +0 -2
- package/dist/lib/database.test.d.ts.map +0 -1
- package/dist/lib/database.test.js +0 -180
- package/dist/lib/database.test.js.map +0 -1
- package/dist/lib/deep-link-payload.test.d.ts +0 -2
- package/dist/lib/deep-link-payload.test.d.ts.map +0 -1
- package/dist/lib/deep-link-payload.test.js +0 -202
- package/dist/lib/deep-link-payload.test.js.map +0 -1
- package/dist/lib/event-emitter.test.d.ts +0 -2
- package/dist/lib/event-emitter.test.d.ts.map +0 -1
- package/dist/lib/event-emitter.test.js +0 -162
- package/dist/lib/event-emitter.test.js.map +0 -1
- package/dist/lib/fingerprint.test.d.ts +0 -2
- package/dist/lib/fingerprint.test.d.ts.map +0 -1
- package/dist/lib/fingerprint.test.js +0 -750
- package/dist/lib/fingerprint.test.js.map +0 -1
- package/dist/lib/launchpad.test.d.ts +0 -2
- package/dist/lib/launchpad.test.d.ts.map +0 -1
- package/dist/lib/launchpad.test.js +0 -368
- package/dist/lib/launchpad.test.js.map +0 -1
- package/dist/lib/link-lookup.test.d.ts +0 -2
- package/dist/lib/link-lookup.test.d.ts.map +0 -1
- package/dist/lib/link-lookup.test.js +0 -61
- package/dist/lib/link-lookup.test.js.map +0 -1
- package/dist/lib/link-resolution-cache.test.d.ts +0 -2
- package/dist/lib/link-resolution-cache.test.d.ts.map +0 -1
- package/dist/lib/link-resolution-cache.test.js +0 -45
- package/dist/lib/link-resolution-cache.test.js.map +0 -1
- package/dist/lib/link-safety.test.d.ts +0 -2
- package/dist/lib/link-safety.test.d.ts.map +0 -1
- package/dist/lib/link-safety.test.js +0 -211
- package/dist/lib/link-safety.test.js.map +0 -1
- package/dist/lib/not-found-page.test.d.ts +0 -2
- package/dist/lib/not-found-page.test.d.ts.map +0 -1
- package/dist/lib/not-found-page.test.js +0 -151
- package/dist/lib/not-found-page.test.js.map +0 -1
- package/dist/lib/portable/cli.test.d.ts +0 -2
- package/dist/lib/portable/cli.test.d.ts.map +0 -1
- package/dist/lib/portable/cli.test.js +0 -98
- package/dist/lib/portable/cli.test.js.map +0 -1
- package/dist/lib/portable/portable.integration.test.d.ts +0 -2
- package/dist/lib/portable/portable.integration.test.d.ts.map +0 -1
- package/dist/lib/portable/portable.integration.test.js +0 -139
- package/dist/lib/portable/portable.integration.test.js.map +0 -1
- package/dist/lib/portable/registry.schema.test.d.ts +0 -2
- package/dist/lib/portable/registry.schema.test.d.ts.map +0 -1
- package/dist/lib/portable/registry.schema.test.js +0 -59
- package/dist/lib/portable/registry.schema.test.js.map +0 -1
- package/dist/lib/scanner-detection.test.d.ts +0 -2
- package/dist/lib/scanner-detection.test.d.ts.map +0 -1
- package/dist/lib/scanner-detection.test.js +0 -124
- package/dist/lib/scanner-detection.test.js.map +0 -1
- package/dist/lib/schema-guard.test.d.ts +0 -2
- package/dist/lib/schema-guard.test.d.ts.map +0 -1
- package/dist/lib/schema-guard.test.js +0 -83
- package/dist/lib/schema-guard.test.js.map +0 -1
- package/dist/lib/utils.test.d.ts +0 -2
- package/dist/lib/utils.test.d.ts.map +0 -1
- package/dist/lib/utils.test.js +0 -261
- package/dist/lib/utils.test.js.map +0 -1
- package/dist/lib/webhook.test.d.ts +0 -2
- package/dist/lib/webhook.test.d.ts.map +0 -1
- package/dist/lib/webhook.test.js +0 -340
- package/dist/lib/webhook.test.js.map +0 -1
- package/dist/routes/health.test.d.ts +0 -2
- package/dist/routes/health.test.d.ts.map +0 -1
- package/dist/routes/health.test.js +0 -82
- package/dist/routes/health.test.js.map +0 -1
- package/dist/routes/preview.test.d.ts +0 -2
- package/dist/routes/preview.test.d.ts.map +0 -1
- package/dist/routes/preview.test.js +0 -111
- package/dist/routes/preview.test.js.map +0 -1
- package/dist/routes/qr.test.d.ts +0 -2
- package/dist/routes/qr.test.d.ts.map +0 -1
- package/dist/routes/qr.test.js +0 -67
- package/dist/routes/qr.test.js.map +0 -1
- package/dist/routes/redirect.click-event.test.d.ts +0 -2
- package/dist/routes/redirect.click-event.test.d.ts.map +0 -1
- package/dist/routes/redirect.click-event.test.js +0 -85
- package/dist/routes/redirect.click-event.test.js.map +0 -1
- package/dist/routes/redirect.host-scope.integration.test.d.ts +0 -2
- package/dist/routes/redirect.host-scope.integration.test.d.ts.map +0 -1
- package/dist/routes/redirect.host-scope.integration.test.js +0 -105
- package/dist/routes/redirect.host-scope.integration.test.js.map +0 -1
- package/dist/routes/redirect.interstitial.test.d.ts +0 -2
- package/dist/routes/redirect.interstitial.test.d.ts.map +0 -1
- package/dist/routes/redirect.interstitial.test.js +0 -128
- package/dist/routes/redirect.interstitial.test.js.map +0 -1
- package/dist/routes/redirect.launchpad.test.d.ts +0 -2
- package/dist/routes/redirect.launchpad.test.d.ts.map +0 -1
- package/dist/routes/redirect.launchpad.test.js +0 -515
- package/dist/routes/redirect.launchpad.test.js.map +0 -1
- package/dist/routes/redirect.no-web-destination.test.d.ts +0 -2
- package/dist/routes/redirect.no-web-destination.test.d.ts.map +0 -1
- package/dist/routes/redirect.no-web-destination.test.js +0 -176
- package/dist/routes/redirect.no-web-destination.test.js.map +0 -1
- package/dist/routes/redirect.not-found.test.d.ts +0 -2
- package/dist/routes/redirect.not-found.test.d.ts.map +0 -1
- package/dist/routes/redirect.not-found.test.js +0 -280
- package/dist/routes/redirect.not-found.test.js.map +0 -1
- package/dist/routes/redirect.safety.test.d.ts +0 -2
- package/dist/routes/redirect.safety.test.d.ts.map +0 -1
- package/dist/routes/redirect.safety.test.js +0 -284
- package/dist/routes/redirect.safety.test.js.map +0 -1
- package/dist/routes/redirect.store-url.test.d.ts +0 -2
- package/dist/routes/redirect.store-url.test.d.ts.map +0 -1
- package/dist/routes/redirect.store-url.test.js +0 -239
- package/dist/routes/redirect.store-url.test.js.map +0 -1
- package/dist/routes/redirect.test.d.ts +0 -2
- package/dist/routes/redirect.test.d.ts.map +0 -1
- package/dist/routes/redirect.test.js +0 -158
- package/dist/routes/redirect.test.js.map +0 -1
- package/dist/routes/redirect.web-link.test.d.ts +0 -2
- package/dist/routes/redirect.web-link.test.d.ts.map +0 -1
- package/dist/routes/redirect.web-link.test.js +0 -138
- package/dist/routes/redirect.web-link.test.js.map +0 -1
- package/dist/routes/sdk-cache-bypass.test.d.ts +0 -2
- package/dist/routes/sdk-cache-bypass.test.d.ts.map +0 -1
- package/dist/routes/sdk-cache-bypass.test.js +0 -175
- package/dist/routes/sdk-cache-bypass.test.js.map +0 -1
- package/dist/routes/sdk.device-values.integration.test.d.ts +0 -2
- package/dist/routes/sdk.device-values.integration.test.d.ts.map +0 -1
- package/dist/routes/sdk.device-values.integration.test.js +0 -132
- package/dist/routes/sdk.device-values.integration.test.js.map +0 -1
- package/dist/routes/sdk.event.test.d.ts +0 -2
- package/dist/routes/sdk.event.test.d.ts.map +0 -1
- package/dist/routes/sdk.event.test.js +0 -284
- package/dist/routes/sdk.event.test.js.map +0 -1
- package/dist/routes/sdk.install-certainty.integration.test.d.ts +0 -2
- package/dist/routes/sdk.install-certainty.integration.test.d.ts.map +0 -1
- package/dist/routes/sdk.install-certainty.integration.test.js +0 -104
- package/dist/routes/sdk.install-certainty.integration.test.js.map +0 -1
- package/dist/routes/sdk.install-live-links.integration.test.d.ts +0 -2
- package/dist/routes/sdk.install-live-links.integration.test.d.ts.map +0 -1
- package/dist/routes/sdk.install-live-links.integration.test.js +0 -92
- package/dist/routes/sdk.install-live-links.integration.test.js.map +0 -1
- package/dist/routes/sdk.install-referrer.integration.test.d.ts +0 -2
- package/dist/routes/sdk.install-referrer.integration.test.d.ts.map +0 -1
- package/dist/routes/sdk.install-referrer.integration.test.js +0 -187
- package/dist/routes/sdk.install-referrer.integration.test.js.map +0 -1
- package/dist/routes/sdk.install-window-once.integration.test.d.ts +0 -2
- package/dist/routes/sdk.install-window-once.integration.test.d.ts.map +0 -1
- package/dist/routes/sdk.install-window-once.integration.test.js +0 -147
- package/dist/routes/sdk.install-window-once.integration.test.js.map +0 -1
- package/dist/routes/sdk.install.test.d.ts +0 -2
- package/dist/routes/sdk.install.test.d.ts.map +0 -1
- package/dist/routes/sdk.install.test.js +0 -99
- package/dist/routes/sdk.install.test.js.map +0 -1
- package/dist/routes/sdk.resolve-click.test.d.ts +0 -2
- package/dist/routes/sdk.resolve-click.test.d.ts.map +0 -1
- package/dist/routes/sdk.resolve-click.test.js +0 -79
- package/dist/routes/sdk.resolve-click.test.js.map +0 -1
- package/dist/routes/sdk.resolve-scope.test.d.ts +0 -2
- package/dist/routes/sdk.resolve-scope.test.d.ts.map +0 -1
- package/dist/routes/sdk.resolve-scope.test.js +0 -105
- package/dist/routes/sdk.resolve-scope.test.js.map +0 -1
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import pg from 'pg';
|
|
2
|
-
import Fastify from 'fastify';
|
|
3
|
-
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
4
|
-
import { db, initializeDatabase } from '../lib/database.js';
|
|
5
|
-
import { sdkRoutes } from './sdk.js';
|
|
6
|
-
/**
|
|
7
|
-
* Whatever a device reports about itself, the SDK endpoints store it.
|
|
8
|
-
*
|
|
9
|
-
* The values below are what real devices and browsers send: locales with
|
|
10
|
-
* Unicode extensions and ICU keywords, long time zone names, metadata from
|
|
11
|
-
* unusual builds. Each is sent through the real routes into a real schema, so
|
|
12
|
-
* a column that cannot hold a reported value fails here rather than on a
|
|
13
|
-
* device. Unit tests with a mocked database cannot see column sizes.
|
|
14
|
-
*
|
|
15
|
-
* Needs a real database; skipped without DATABASE_URL, like database.test.ts.
|
|
16
|
-
*/
|
|
17
|
-
const ADMIN_URL = process.env.DATABASE_URL;
|
|
18
|
-
/** Device languages as platforms report them, and what is stored. */
|
|
19
|
-
const DEVICE_LANGUAGES = [
|
|
20
|
-
['en-US', 'en-US'],
|
|
21
|
-
// Android Locale.toLanguageTag() with regional preferences set
|
|
22
|
-
['en-ES-u-fw-mon-mu-celsius', 'en-ES'],
|
|
23
|
-
['de-DE-u-ca-gregory-fw-mon-mu-celsius-nu-latn', 'de-DE'],
|
|
24
|
-
// Android Locale.toString()
|
|
25
|
-
['en_ES_#u-fw-mon-mu-celsius', 'en-ES'],
|
|
26
|
-
['zh_CN_#Hans', 'zh-CN'],
|
|
27
|
-
// iOS Locale.identifier
|
|
28
|
-
['en_GB@rg=eszzzz', 'en-GB'],
|
|
29
|
-
['en_US@calendar=japanese', 'en-US'],
|
|
30
|
-
['th_TH@numbers=thai;calendar=buddhist', 'th-TH'],
|
|
31
|
-
// Script subtags and long tags
|
|
32
|
-
['zh-Hans-CN', 'zh-CN'],
|
|
33
|
-
['yue-Hant-HK', 'yue-HK'],
|
|
34
|
-
['sr-Latn-RS', 'sr-RS'],
|
|
35
|
-
['es-419', 'es-419'],
|
|
36
|
-
['fil-PH', 'fil-PH'],
|
|
37
|
-
['ca-ES-valencia', 'ca-ES'],
|
|
38
|
-
// POSIX
|
|
39
|
-
['en_US.UTF-8', 'en-US'],
|
|
40
|
-
// Not a language
|
|
41
|
-
['*', null],
|
|
42
|
-
['x'.repeat(300), null],
|
|
43
|
-
];
|
|
44
|
-
const baseInstall = {
|
|
45
|
-
userAgent: 'App/1.0 android/15',
|
|
46
|
-
timezone: 'Europe/Madrid',
|
|
47
|
-
screenWidth: 1080,
|
|
48
|
-
screenHeight: 2400,
|
|
49
|
-
platform: 'android',
|
|
50
|
-
platformVersion: '15',
|
|
51
|
-
sdkName: 'android',
|
|
52
|
-
sdkVersion: '1.0.0',
|
|
53
|
-
};
|
|
54
|
-
describe.skipIf(!ADMIN_URL)('SDK endpoints store any device-reported value (integration)', () => {
|
|
55
|
-
const dbName = `lf_core_device_values_${Date.now()}`;
|
|
56
|
-
let admin;
|
|
57
|
-
let app;
|
|
58
|
-
beforeAll(async () => {
|
|
59
|
-
admin = new pg.Client({ connectionString: ADMIN_URL });
|
|
60
|
-
await admin.connect();
|
|
61
|
-
await admin.query(`CREATE DATABASE ${dbName}`);
|
|
62
|
-
const url = new URL(ADMIN_URL);
|
|
63
|
-
url.pathname = `/${dbName}`;
|
|
64
|
-
await initializeDatabase({ url: url.toString(), pool: { min: 1, max: 3 } });
|
|
65
|
-
app = Fastify();
|
|
66
|
-
await app.register(sdkRoutes);
|
|
67
|
-
await app.ready();
|
|
68
|
-
}, 120000);
|
|
69
|
-
afterAll(async () => {
|
|
70
|
-
await app?.close();
|
|
71
|
-
await db.end().catch(() => undefined);
|
|
72
|
-
await admin.query(`DROP DATABASE IF EXISTS ${dbName} WITH (FORCE)`);
|
|
73
|
-
await admin.end();
|
|
74
|
-
}, 60000);
|
|
75
|
-
async function install(body) {
|
|
76
|
-
return app.inject({ method: 'POST', url: '/api/sdk/v1/install', payload: { ...baseInstall, ...body } });
|
|
77
|
-
}
|
|
78
|
-
it.each(DEVICE_LANGUAGES)('records an install reporting language %j', async (reported, stored) => {
|
|
79
|
-
const deviceId = `lang-${Buffer.from(reported.slice(0, 40)).toString('hex')}`;
|
|
80
|
-
const res = await install({ language: reported, deviceId });
|
|
81
|
-
expect(res.statusCode).toBe(200);
|
|
82
|
-
const row = await db.query('SELECT language FROM install_events WHERE id = $1', [res.json().installId]);
|
|
83
|
-
expect(row.rows[0].language).toBe(stored);
|
|
84
|
-
});
|
|
85
|
-
it('records an install whose every text field is longer than its column', async () => {
|
|
86
|
-
const res = await install({
|
|
87
|
-
language: 'en-ES-u-fw-mon-mu-celsius',
|
|
88
|
-
timezone: 'T'.repeat(500),
|
|
89
|
-
platform: 'P'.repeat(500),
|
|
90
|
-
platformVersion: 'V'.repeat(500),
|
|
91
|
-
userAgent: 'U'.repeat(5000),
|
|
92
|
-
deviceId: 'D'.repeat(1000),
|
|
93
|
-
sdkName: 'N'.repeat(500),
|
|
94
|
-
sdkVersion: 'S'.repeat(500),
|
|
95
|
-
screenWidth: 1e12,
|
|
96
|
-
screenHeight: 2400.5,
|
|
97
|
-
attributionWindowHours: 1e12,
|
|
98
|
-
});
|
|
99
|
-
expect(res.statusCode).toBe(200);
|
|
100
|
-
const row = await db.query(`SELECT length(timezone) AS tz, length(platform) AS p, length(platform_version) AS pv,
|
|
101
|
-
length(device_id) AS d, length(sdk_name) AS n, length(sdk_version) AS v,
|
|
102
|
-
screen_width, screen_height, attribution_window_hours
|
|
103
|
-
FROM install_events WHERE id = $1`, [res.json().installId]);
|
|
104
|
-
expect(row.rows[0]).toMatchObject({ tz: 100, p: 50, pv: 50, d: 255, n: 50, v: 50, screen_width: null, screen_height: 2401 });
|
|
105
|
-
expect(row.rows[0].attribution_window_hours).toBe(168);
|
|
106
|
-
});
|
|
107
|
-
it.each(DEVICE_LANGUAGES.map(([reported, stored], i) => [reported, stored, i]))('stores the click fingerprint of a resolve reporting language %j', async (reported, stored, i) => {
|
|
108
|
-
// A link per case, so the fingerprint is found by its link rather than by time.
|
|
109
|
-
const code = `devvals${i}`;
|
|
110
|
-
await db.query(`INSERT INTO links (short_code, original_url) VALUES ($1, 'https://example.com/landing')`, [code]);
|
|
111
|
-
const res = await app.inject({
|
|
112
|
-
method: 'GET',
|
|
113
|
-
url: `/api/sdk/v1/resolve/${code}?fp_lang=${encodeURIComponent(reported)}&fp_tz=${'Z'.repeat(300)}&fp_sw=99999999999&fp_sh=2400`,
|
|
114
|
-
headers: { 'user-agent': 'Mozilla/5.0 (Linux; Android 15; Pixel 8)' },
|
|
115
|
-
});
|
|
116
|
-
expect(res.statusCode).toBe(200);
|
|
117
|
-
// The click and its fingerprint are written after the response.
|
|
118
|
-
let rows = [];
|
|
119
|
-
for (let attempt = 0; attempt < 100 && rows.length === 0; attempt++) {
|
|
120
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
121
|
-
const result = await db.query(`SELECT f.language, length(f.timezone) AS tz, f.screen_width
|
|
122
|
-
FROM device_fingerprints f
|
|
123
|
-
JOIN click_events c ON c.id = f.click_id
|
|
124
|
-
JOIN links l ON l.id = c.link_id
|
|
125
|
-
WHERE l.short_code = $1`, [code]);
|
|
126
|
-
rows = result.rows;
|
|
127
|
-
}
|
|
128
|
-
expect(rows).toHaveLength(1);
|
|
129
|
-
expect(rows[0]).toMatchObject({ language: stored, tz: 100, screen_width: null });
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
//# sourceMappingURL=sdk.device-values.integration.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.device-values.integration.test.js","sourceRoot":"","sources":["../../src/routes/sdk.device-values.integration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.event.test.d.ts","sourceRoot":"","sources":["../../src/routes/sdk.event.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
-
// Mock the database singleton so the route runs without a real Postgres.
|
|
3
|
-
vi.mock('../lib/database.js', () => ({
|
|
4
|
-
db: { query: vi.fn() },
|
|
5
|
-
}));
|
|
6
|
-
import Fastify from 'fastify';
|
|
7
|
-
import { db } from '../lib/database.js';
|
|
8
|
-
import { sdkRoutes } from './sdk.js';
|
|
9
|
-
const mockQuery = db.query;
|
|
10
|
-
const INSTALL_ID = '11111111-1111-4111-8111-111111111111';
|
|
11
|
-
const LINK_ID = '22222222-2222-4222-8222-222222222222';
|
|
12
|
-
const SESSION_ID = '33333333-3333-4333-8333-333333333333';
|
|
13
|
-
const CLICK_ID = '44444444-4444-4444-8444-444444444444';
|
|
14
|
-
const EVENT_ID = '55555555-5555-4555-8555-555555555555';
|
|
15
|
-
async function buildApp() {
|
|
16
|
-
const app = Fastify();
|
|
17
|
-
await app.register(sdkRoutes);
|
|
18
|
-
await app.ready();
|
|
19
|
-
return app;
|
|
20
|
-
}
|
|
21
|
-
const EVENT_TS = '2026-06-05T10:05:00.000Z';
|
|
22
|
-
const LINK_OPENED_AT = '2026-06-05T10:00:00.000Z';
|
|
23
|
-
const stampedEvent = {
|
|
24
|
-
installId: INSTALL_ID,
|
|
25
|
-
eventName: 'add_to_cart',
|
|
26
|
-
eventData: { sku: 'abc' },
|
|
27
|
-
timestamp: EVENT_TS,
|
|
28
|
-
attributedLinkId: LINK_ID,
|
|
29
|
-
attributedClickId: CLICK_ID,
|
|
30
|
-
linkOpenedAt: LINK_OPENED_AT,
|
|
31
|
-
sessionId: SESSION_ID,
|
|
32
|
-
sdkName: 'react-native',
|
|
33
|
-
sdkVersion: '1.4.0',
|
|
34
|
-
};
|
|
35
|
-
describe('POST /api/sdk/v1/event — last-click attribution stamp', () => {
|
|
36
|
-
beforeEach(() => {
|
|
37
|
-
mockQuery.mockReset();
|
|
38
|
-
});
|
|
39
|
-
it('persists the attribution stamp on the in_app_events row', async () => {
|
|
40
|
-
// 1) install lookup (link_id null so the webhook block is skipped)
|
|
41
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: INSTALL_ID, link_id: null }] });
|
|
42
|
-
// 2) the event INSERT
|
|
43
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: EVENT_ID }] });
|
|
44
|
-
const app = await buildApp();
|
|
45
|
-
const res = await app.inject({ method: 'POST', url: '/api/sdk/v1/event', payload: stampedEvent });
|
|
46
|
-
expect(res.statusCode).toBe(200);
|
|
47
|
-
expect(res.json()).toMatchObject({ eventId: EVENT_ID, acknowledged: true });
|
|
48
|
-
const insertCall = mockQuery.mock.calls[1];
|
|
49
|
-
expect(insertCall[0]).toMatch(/INSERT INTO in_app_events/);
|
|
50
|
-
expect(insertCall[0]).toMatch(/attributed_link_id/);
|
|
51
|
-
// params: install, name, dataJson, ts, link, click, openedAt, session, sdk name/version, client event id
|
|
52
|
-
expect(insertCall[1]).toEqual([
|
|
53
|
-
INSTALL_ID,
|
|
54
|
-
'add_to_cart',
|
|
55
|
-
JSON.stringify({ sku: 'abc' }),
|
|
56
|
-
EVENT_TS,
|
|
57
|
-
LINK_ID,
|
|
58
|
-
CLICK_ID,
|
|
59
|
-
LINK_OPENED_AT,
|
|
60
|
-
SESSION_ID,
|
|
61
|
-
'react-native',
|
|
62
|
-
'1.4.0',
|
|
63
|
-
null,
|
|
64
|
-
]);
|
|
65
|
-
await app.close();
|
|
66
|
-
});
|
|
67
|
-
it('stays backward compatible: an event with no stamp stores null attribution + a sessionId-less row', async () => {
|
|
68
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: INSTALL_ID, link_id: null }] });
|
|
69
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: EVENT_ID }] });
|
|
70
|
-
const app = await buildApp();
|
|
71
|
-
const res = await app.inject({
|
|
72
|
-
method: 'POST',
|
|
73
|
-
url: '/api/sdk/v1/event',
|
|
74
|
-
payload: { installId: INSTALL_ID, eventName: 'signup' },
|
|
75
|
-
});
|
|
76
|
-
expect(res.statusCode).toBe(200);
|
|
77
|
-
const params = mockQuery.mock.calls[1][1];
|
|
78
|
-
expect(params[4]).toBeNull(); // attributed_link_id
|
|
79
|
-
expect(params[5]).toBeNull(); // attributed_click_id
|
|
80
|
-
expect(params[6]).toBeNull(); // attributed_at
|
|
81
|
-
expect(params[7]).toBeNull(); // session_id
|
|
82
|
-
await app.close();
|
|
83
|
-
});
|
|
84
|
-
it('never loses an event when the attributed link is stale: falls back to no-link insert on FK violation', async () => {
|
|
85
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: INSTALL_ID, link_id: null }] });
|
|
86
|
-
// first INSERT rejects with the attributed_link_id FK violation
|
|
87
|
-
mockQuery.mockRejectedValueOnce(Object.assign(new Error('FK violation'), { code: '23503', constraint: 'in_app_events_attributed_link_id_fkey' }));
|
|
88
|
-
// fallback INSERT (without attributed_link_id) succeeds
|
|
89
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: EVENT_ID }] });
|
|
90
|
-
const app = await buildApp();
|
|
91
|
-
const res = await app.inject({ method: 'POST', url: '/api/sdk/v1/event', payload: stampedEvent });
|
|
92
|
-
expect(res.statusCode).toBe(200);
|
|
93
|
-
expect(res.json()).toMatchObject({ eventId: EVENT_ID, acknowledged: true });
|
|
94
|
-
// the fallback INSERT omits attributed_link_id but keeps click/session
|
|
95
|
-
const fallbackCall = mockQuery.mock.calls[2];
|
|
96
|
-
expect(fallbackCall[0]).not.toMatch(/attributed_link_id/);
|
|
97
|
-
expect(fallbackCall[1]).toEqual([
|
|
98
|
-
INSTALL_ID,
|
|
99
|
-
'add_to_cart',
|
|
100
|
-
JSON.stringify({ sku: 'abc' }),
|
|
101
|
-
EVENT_TS,
|
|
102
|
-
CLICK_ID,
|
|
103
|
-
LINK_OPENED_AT,
|
|
104
|
-
SESSION_ID,
|
|
105
|
-
'react-native',
|
|
106
|
-
'1.4.0',
|
|
107
|
-
null,
|
|
108
|
-
]);
|
|
109
|
-
await app.close();
|
|
110
|
-
});
|
|
111
|
-
it('rethrows a non-link FK violation (e.g. install deleted mid-request) instead of mislabeling it as a link problem', async () => {
|
|
112
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: INSTALL_ID, link_id: null }] });
|
|
113
|
-
// install_id's FK lost to a concurrent install delete — a 23503 that is NOT the link FK
|
|
114
|
-
mockQuery.mockRejectedValueOnce(Object.assign(new Error('FK violation'), { code: '23503', constraint: 'in_app_events_install_id_fkey' }));
|
|
115
|
-
const app = await buildApp();
|
|
116
|
-
const res = await app.inject({ method: 'POST', url: '/api/sdk/v1/event', payload: stampedEvent });
|
|
117
|
-
// Surfaces as a real error; no misleading no-link fallback insert is attempted.
|
|
118
|
-
expect(res.statusCode).toBe(500);
|
|
119
|
-
expect(mockQuery.mock.calls.length).toBe(2); // install lookup + the failed insert only
|
|
120
|
-
await app.close();
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
/**
|
|
124
|
-
* An event whose install is gone.
|
|
125
|
-
*
|
|
126
|
-
* The install id lives in the app's storage for the life of the install, so a
|
|
127
|
-
* missing row is permanent from the device's point of view: refusing the event
|
|
128
|
-
* silences that app forever. A deployment that prunes analytics on a retention
|
|
129
|
-
* window reaches this the moment an install outlives the window.
|
|
130
|
-
*/
|
|
131
|
-
describe('POST /api/sdk/v1/event — an install the server no longer has', () => {
|
|
132
|
-
beforeEach(() => {
|
|
133
|
-
mockQuery.mockReset();
|
|
134
|
-
});
|
|
135
|
-
const orphanEvent = {
|
|
136
|
-
installId: INSTALL_ID,
|
|
137
|
-
eventName: 'purchase',
|
|
138
|
-
eventData: { value: 12 },
|
|
139
|
-
sdkName: 'android',
|
|
140
|
-
sdkVersion: '1.3.2',
|
|
141
|
-
};
|
|
142
|
-
it('records the install again and stores the event against it', async () => {
|
|
143
|
-
mockQuery.mockResolvedValueOnce({ rows: [] }); // 1) install lookup: gone
|
|
144
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: INSTALL_ID, link_id: null }] }); // 2) recovery insert
|
|
145
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: EVENT_ID }] }); // 3) the event insert
|
|
146
|
-
const app = await buildApp();
|
|
147
|
-
const res = await app.inject({ method: 'POST', url: '/api/sdk/v1/event', payload: orphanEvent });
|
|
148
|
-
expect(res.statusCode).toBe(200);
|
|
149
|
-
expect(res.json()).toMatchObject({ eventId: EVENT_ID, acknowledged: true });
|
|
150
|
-
const recovery = mockQuery.mock.calls[1];
|
|
151
|
-
expect(recovery[0]).toMatch(/INSERT INTO install_events/);
|
|
152
|
-
// The id the client sent, so the device's next event resolves normally.
|
|
153
|
-
expect(recovery[1][0]).toBe(INSTALL_ID);
|
|
154
|
-
// A marker, never a real hash: it cannot collide with a fingerprint match.
|
|
155
|
-
expect(recovery[1][1]).toBe(`recovered:${INSTALL_ID}`);
|
|
156
|
-
expect(recovery[1].slice(2)).toEqual(['android', '1.3.2']);
|
|
157
|
-
// Concurrent events for the same install must not collide.
|
|
158
|
-
expect(recovery[0]).toMatch(/ON CONFLICT \(id\) DO NOTHING/);
|
|
159
|
-
// The event is stored against that install, unattributed.
|
|
160
|
-
const insert = mockQuery.mock.calls[2];
|
|
161
|
-
expect(insert[0]).toMatch(/INSERT INTO in_app_events/);
|
|
162
|
-
expect(insert[1][0]).toBe(INSTALL_ID);
|
|
163
|
-
await app.close();
|
|
164
|
-
});
|
|
165
|
-
it('claims no attribution for a recovered install', async () => {
|
|
166
|
-
mockQuery.mockResolvedValueOnce({ rows: [] });
|
|
167
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: INSTALL_ID, link_id: null }] });
|
|
168
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: EVENT_ID }] });
|
|
169
|
-
const app = await buildApp();
|
|
170
|
-
await app.inject({ method: 'POST', url: '/api/sdk/v1/event', payload: orphanEvent });
|
|
171
|
-
const sql = mockQuery.mock.calls[1][0];
|
|
172
|
-
expect(sql).toMatch(/'recovered'/);
|
|
173
|
-
// No link, click or confidence among the columns written: a device we have
|
|
174
|
-
// met before is not a new attributed install, and nothing downstream may
|
|
175
|
-
// read it as one. (The RETURNING clause reads link_id back; that is not a
|
|
176
|
-
// write.)
|
|
177
|
-
const columns = sql.slice(sql.indexOf('('), sql.indexOf('VALUES'));
|
|
178
|
-
expect(columns).not.toMatch(/link_id|click_id|confidence_score/);
|
|
179
|
-
await app.close();
|
|
180
|
-
});
|
|
181
|
-
it('uses the row a concurrent request created rather than failing', async () => {
|
|
182
|
-
mockQuery.mockResolvedValueOnce({ rows: [] }); // lookup: gone
|
|
183
|
-
mockQuery.mockResolvedValueOnce({ rows: [] }); // insert: lost the race
|
|
184
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: INSTALL_ID, link_id: LINK_ID }] }); // re-read
|
|
185
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: EVENT_ID }] }); // event insert
|
|
186
|
-
mockQuery.mockResolvedValueOnce({ rows: [] }); // webhook lookup (link_id set)
|
|
187
|
-
const app = await buildApp();
|
|
188
|
-
const res = await app.inject({ method: 'POST', url: '/api/sdk/v1/event', payload: orphanEvent });
|
|
189
|
-
expect(res.statusCode).toBe(200);
|
|
190
|
-
expect(res.json()).toMatchObject({ eventId: EVENT_ID, acknowledged: true });
|
|
191
|
-
await app.close();
|
|
192
|
-
});
|
|
193
|
-
it('tells a client what to do when recovery cannot complete', async () => {
|
|
194
|
-
mockQuery.mockResolvedValueOnce({ rows: [] }); // lookup: gone
|
|
195
|
-
mockQuery.mockResolvedValueOnce({ rows: [] }); // insert: no row
|
|
196
|
-
mockQuery.mockResolvedValueOnce({ rows: [] }); // re-read: still nothing
|
|
197
|
-
const app = await buildApp();
|
|
198
|
-
const res = await app.inject({ method: 'POST', url: '/api/sdk/v1/event', payload: orphanEvent });
|
|
199
|
-
expect(res.statusCode).toBe(404);
|
|
200
|
-
expect(res.json()).toEqual({
|
|
201
|
-
error: 'Install event not found',
|
|
202
|
-
code: 'INSTALL_NOT_FOUND',
|
|
203
|
-
action: 'reregister',
|
|
204
|
-
});
|
|
205
|
-
await app.close();
|
|
206
|
-
});
|
|
207
|
-
it('leaves a known install untouched', async () => {
|
|
208
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: INSTALL_ID, link_id: null }] });
|
|
209
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: EVENT_ID }] });
|
|
210
|
-
const app = await buildApp();
|
|
211
|
-
const res = await app.inject({ method: 'POST', url: '/api/sdk/v1/event', payload: orphanEvent });
|
|
212
|
-
expect(res.statusCode).toBe(200);
|
|
213
|
-
// Two queries only: the lookup and the event. No recovery insert.
|
|
214
|
-
expect(mockQuery.mock.calls).toHaveLength(2);
|
|
215
|
-
expect(mockQuery.mock.calls[1][0]).toMatch(/INSERT INTO in_app_events/);
|
|
216
|
-
await app.close();
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
describe('POST /api/sdk/v1/event — a repeat of an event already stored', () => {
|
|
220
|
-
const CLIENT_EVENT_ID = '66666666-6666-4666-8666-666666666666';
|
|
221
|
-
const event = { installId: INSTALL_ID, eventName: 'purchase', clientEventId: CLIENT_EVENT_ID };
|
|
222
|
-
beforeEach(() => {
|
|
223
|
-
mockQuery.mockReset();
|
|
224
|
-
});
|
|
225
|
-
it('stores a first event with its client event id', async () => {
|
|
226
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: INSTALL_ID, link_id: null }] }); // install
|
|
227
|
-
mockQuery.mockResolvedValueOnce({ rows: [] }); // no stored repeat
|
|
228
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: EVENT_ID }] }); // INSERT
|
|
229
|
-
const app = await buildApp();
|
|
230
|
-
const res = await app.inject({ method: 'POST', url: '/api/sdk/v1/event', payload: event });
|
|
231
|
-
expect(res.statusCode).toBe(200);
|
|
232
|
-
expect(res.json()).toEqual({ eventId: EVENT_ID, acknowledged: true });
|
|
233
|
-
const insert = mockQuery.mock.calls[2];
|
|
234
|
-
expect(insert[0]).toMatch(/INSERT INTO in_app_events/);
|
|
235
|
-
expect(insert[1].at(-1)).toBe(CLIENT_EVENT_ID);
|
|
236
|
-
await app.close();
|
|
237
|
-
});
|
|
238
|
-
it('answers a repeat with the stored event and records nothing', async () => {
|
|
239
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: INSTALL_ID, link_id: LINK_ID }] }); // install, attributed
|
|
240
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: EVENT_ID }] }); // stored repeat
|
|
241
|
-
const app = await buildApp();
|
|
242
|
-
const res = await app.inject({ method: 'POST', url: '/api/sdk/v1/event', payload: event });
|
|
243
|
-
expect(res.statusCode).toBe(200);
|
|
244
|
-
expect(res.json()).toEqual({ eventId: EVENT_ID, acknowledged: true, duplicate: true });
|
|
245
|
-
expect(mockQuery.mock.calls.some(([sql]) => /INSERT INTO in_app_events/.test(String(sql)))).toBe(false);
|
|
246
|
-
expect(mockQuery.mock.calls.some(([sql]) => /webhooks/i.test(String(sql)))).toBe(false);
|
|
247
|
-
await app.close();
|
|
248
|
-
});
|
|
249
|
-
it('answers the second of two copies arriving at once as a repeat', async () => {
|
|
250
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: INSTALL_ID, link_id: null }] }); // install
|
|
251
|
-
mockQuery.mockResolvedValueOnce({ rows: [] }); // not stored yet
|
|
252
|
-
mockQuery.mockRejectedValueOnce(Object.assign(new Error('duplicate key'), { code: '23505', constraint: 'idx_in_app_events_client_event' })); // the other copy won the INSERT
|
|
253
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: EVENT_ID }] }); // stored by the other copy
|
|
254
|
-
const app = await buildApp();
|
|
255
|
-
const res = await app.inject({ method: 'POST', url: '/api/sdk/v1/event', payload: event });
|
|
256
|
-
expect(res.statusCode).toBe(200);
|
|
257
|
-
expect(res.json()).toEqual({ eventId: EVENT_ID, acknowledged: true, duplicate: true });
|
|
258
|
-
await app.close();
|
|
259
|
-
});
|
|
260
|
-
it('does not look for a repeat when the event has no client event id', async () => {
|
|
261
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: INSTALL_ID, link_id: null }] }); // install
|
|
262
|
-
mockQuery.mockResolvedValueOnce({ rows: [{ id: EVENT_ID }] }); // INSERT
|
|
263
|
-
const app = await buildApp();
|
|
264
|
-
const res = await app.inject({
|
|
265
|
-
method: 'POST',
|
|
266
|
-
url: '/api/sdk/v1/event',
|
|
267
|
-
payload: { installId: INSTALL_ID, eventName: 'purchase' },
|
|
268
|
-
});
|
|
269
|
-
expect(res.json()).toEqual({ eventId: EVENT_ID, acknowledged: true });
|
|
270
|
-
expect(mockQuery.mock.calls.some(([sql]) => /client_event_id = \$2/.test(String(sql)))).toBe(false);
|
|
271
|
-
await app.close();
|
|
272
|
-
});
|
|
273
|
-
it('rejects a client event id that is not a UUID', async () => {
|
|
274
|
-
const app = await buildApp();
|
|
275
|
-
const res = await app.inject({
|
|
276
|
-
method: 'POST',
|
|
277
|
-
url: '/api/sdk/v1/event',
|
|
278
|
-
payload: { ...event, clientEventId: 'not-a-uuid' },
|
|
279
|
-
});
|
|
280
|
-
expect(res.statusCode).not.toBe(200);
|
|
281
|
-
await app.close();
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
//# sourceMappingURL=sdk.event.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.event.test.js","sourceRoot":"","sources":["../../src/routes/sdk.event.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.install-certainty.integration.test.d.ts","sourceRoot":"","sources":["../../src/routes/sdk.install-certainty.integration.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import pg from 'pg';
|
|
2
|
-
import Fastify from 'fastify';
|
|
3
|
-
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
4
|
-
import { db, initializeDatabase } from '../lib/database.js';
|
|
5
|
-
import { linkRoutes } from './links.js';
|
|
6
|
-
import { sdkRoutes } from './sdk.js';
|
|
7
|
-
/**
|
|
8
|
-
* The install response says how the install was matched and how many other
|
|
9
|
-
* links were in contention, and the install row records the same.
|
|
10
|
-
*
|
|
11
|
-
* Each case clicks from its own /24, so it can only match its own link.
|
|
12
|
-
* Needs a real database; skipped without DATABASE_URL, like database.test.ts.
|
|
13
|
-
*/
|
|
14
|
-
const ADMIN_URL = process.env.DATABASE_URL;
|
|
15
|
-
const clickUserAgent = 'Mozilla/5.0 (Linux; Android 15; Pixel 8) AppleWebKit/537.36 Chrome/130.0 Mobile Safari/537.36';
|
|
16
|
-
const installBody = {
|
|
17
|
-
userAgent: 'App/1.0 Android/15',
|
|
18
|
-
timezone: 'Europe/Madrid',
|
|
19
|
-
language: 'en-ES',
|
|
20
|
-
screenWidth: 1080,
|
|
21
|
-
screenHeight: 2400,
|
|
22
|
-
platform: 'android',
|
|
23
|
-
platformVersion: '15',
|
|
24
|
-
};
|
|
25
|
-
describe.skipIf(!ADMIN_URL)('install match certainty (integration)', () => {
|
|
26
|
-
const dbName = `lf_core_certainty_${Date.now()}`;
|
|
27
|
-
let admin;
|
|
28
|
-
let app;
|
|
29
|
-
beforeAll(async () => {
|
|
30
|
-
admin = new pg.Client({ connectionString: ADMIN_URL });
|
|
31
|
-
await admin.connect();
|
|
32
|
-
await admin.query(`CREATE DATABASE ${dbName}`);
|
|
33
|
-
const url = new URL(ADMIN_URL);
|
|
34
|
-
url.pathname = `/${dbName}`;
|
|
35
|
-
await initializeDatabase({ url: url.toString(), pool: { min: 1, max: 3 } });
|
|
36
|
-
app = Fastify();
|
|
37
|
-
await app.register(linkRoutes);
|
|
38
|
-
await app.register(sdkRoutes);
|
|
39
|
-
await app.ready();
|
|
40
|
-
}, 120000);
|
|
41
|
-
afterAll(async () => {
|
|
42
|
-
await app?.close();
|
|
43
|
-
await db.end().catch(() => undefined);
|
|
44
|
-
await admin.query(`DROP DATABASE IF EXISTS ${dbName} WITH (FORCE)`);
|
|
45
|
-
await admin.end();
|
|
46
|
-
}, 60000);
|
|
47
|
-
async function createLink(body) {
|
|
48
|
-
const res = await app.inject({
|
|
49
|
-
method: 'POST',
|
|
50
|
-
url: '/api/links',
|
|
51
|
-
payload: { originalUrl: 'https://example.com/landing', ...body },
|
|
52
|
-
});
|
|
53
|
-
expect(res.statusCode).toBe(200);
|
|
54
|
-
return res.json();
|
|
55
|
-
}
|
|
56
|
-
/** A click on the link from `network`.1, `minutesAgo` minutes ago by the database clock. */
|
|
57
|
-
async function click(linkId, network, minutesAgo) {
|
|
58
|
-
const ce = await db.query(`INSERT INTO click_events (link_id, clicked_at, ip_address, user_agent, platform)
|
|
59
|
-
VALUES ($1, NOW() - make_interval(mins => $2), $3, $4, 'android') RETURNING id`, [linkId, minutesAgo, `${network}.1`, clickUserAgent]);
|
|
60
|
-
await db.query(`INSERT INTO device_fingerprints
|
|
61
|
-
(click_id, fingerprint_hash, ip_address, user_agent, timezone, language, screen_width, screen_height, platform, platform_version)
|
|
62
|
-
VALUES ($1, 'h', $2, $3, 'Europe/Madrid', 'en-ES', 412, 915, 'android', '15')`, [ce.rows[0].id, `${network}.1`, clickUserAgent]);
|
|
63
|
-
return ce.rows[0].id;
|
|
64
|
-
}
|
|
65
|
-
async function installFrom(network, host = 2) {
|
|
66
|
-
const res = await app.inject({
|
|
67
|
-
method: 'POST',
|
|
68
|
-
url: '/api/sdk/v1/install',
|
|
69
|
-
payload: installBody,
|
|
70
|
-
remoteAddress: `${network}.${host}`,
|
|
71
|
-
});
|
|
72
|
-
expect(res.statusCode).toBe(200);
|
|
73
|
-
return res.json();
|
|
74
|
-
}
|
|
75
|
-
async function storedCompetingLinks(installId) {
|
|
76
|
-
const row = await db.query('SELECT attribution_method, competing_links FROM install_events WHERE id = $1', [installId]);
|
|
77
|
-
return row.rows[0];
|
|
78
|
-
}
|
|
79
|
-
it('reports no competing links when one link was clicked', async () => {
|
|
80
|
-
const link = await createLink({});
|
|
81
|
-
await click(link.id, '81.2.90', 5);
|
|
82
|
-
const body = await installFrom('81.2.90');
|
|
83
|
-
expect(body).toMatchObject({ attributed: true, attributionMethod: 'fingerprint', competingLinks: 0 });
|
|
84
|
-
expect(body.deepLinkData).toMatchObject({ attributionMethod: 'fingerprint', competingLinks: 0 });
|
|
85
|
-
expect(await storedCompetingLinks(body.installId)).toEqual({ attribution_method: 'fingerprint', competing_links: 0 });
|
|
86
|
-
});
|
|
87
|
-
it('counts another link clicked from the same network by a matching device', async () => {
|
|
88
|
-
const mine = await createLink({});
|
|
89
|
-
const theirs = await createLink({});
|
|
90
|
-
await click(mine.id, '81.2.91', 6);
|
|
91
|
-
await click(theirs.id, '81.2.91', 4);
|
|
92
|
-
await click(theirs.id, '81.2.91', 3);
|
|
93
|
-
const body = await installFrom('81.2.91');
|
|
94
|
-
expect(body).toMatchObject({ attributed: true, competingLinks: 1 });
|
|
95
|
-
expect(body.deepLinkData.competingLinks).toBe(1);
|
|
96
|
-
expect((await storedCompetingLinks(body.installId)).competing_links).toBe(1);
|
|
97
|
-
});
|
|
98
|
-
it('reports an organic install as none', async () => {
|
|
99
|
-
const body = await installFrom('81.2.92');
|
|
100
|
-
expect(body).toMatchObject({ attributed: false, attributionMethod: 'none', competingLinks: 0, deepLinkData: null });
|
|
101
|
-
expect(await storedCompetingLinks(body.installId)).toEqual({ attribution_method: 'none', competing_links: null });
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
//# sourceMappingURL=sdk.install-certainty.integration.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.install-certainty.integration.test.js","sourceRoot":"","sources":["../../src/routes/sdk.install-certainty.integration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.install-live-links.integration.test.d.ts","sourceRoot":"","sources":["../../src/routes/sdk.install-live-links.integration.test.ts"],"names":[],"mappings":""}
|