579 lines
21 KiB
TypeScript
579 lines
21 KiB
TypeScript
import { describe, it, expect, beforeEach } from 'vitest';
|
|
import { NextRequest } from 'next/server';
|
|
import { POST as previewPost } from '../src/app/api/posts/preview/route';
|
|
import { GET as giveawayDetailGet } from '../src/app/api/giveaways/[id]/route';
|
|
import { MemoryUserRepository, setUserRepository } from '../src/lib/repository/user-repository';
|
|
import { defaultSessionStore, SESSION_COOKIE_NAME } from '../src/lib/auth/session';
|
|
import { AesGcmTokenVault } from '../src/lib/auth/token-vault';
|
|
import { TokenRefresher } from '../src/lib/auth/token-refresher';
|
|
import { VkAuthContextResolver } from '../src/integrations/vk/vk-auth-resolver';
|
|
import { VkProvider } from '../src/providers/vk/vk-provider';
|
|
import { IVkClient } from '../src/integrations/vk/vk-client';
|
|
import { VkAuthContext } from '../src/integrations/vk/vk-types';
|
|
import { VkPrivateResourceError } from '../src/integrations/vk/vk-errors';
|
|
import { ProviderFactory } from '../src/providers/factory';
|
|
import { GiveawayStore } from '../src/lib/giveaway-store';
|
|
import { MemoryGiveawayRepository } from '../src/lib/repository/memory-repository';
|
|
import { DEFAULT_FILTER_RULES } from '../src/core/types/giveaway';
|
|
|
|
describe('Phase 2.3.1 — Effective Capabilities Truthfulness Gate', () => {
|
|
let userRepo: MemoryUserRepository;
|
|
let tokenVault: AesGcmTokenVault;
|
|
let tokenRefresher: TokenRefresher;
|
|
let authResolver: VkAuthContextResolver;
|
|
|
|
let loggedInUser: any;
|
|
let sessionCookie: string;
|
|
let loggedInUserWithoutCreds: any;
|
|
let sessionCookieNoCreds: string;
|
|
|
|
const userTokenPlain = 'vk1.a.user_organizer_valid_token_123';
|
|
const serviceTokenPlain = 'vk_service_token_456';
|
|
|
|
beforeEach(async () => {
|
|
userRepo = new MemoryUserRepository();
|
|
setUserRepository(userRepo);
|
|
|
|
defaultSessionStore.clear();
|
|
|
|
tokenVault = new AesGcmTokenVault('test-master-token-encryption-key-32b!');
|
|
tokenRefresher = new TokenRefresher(userRepo, tokenVault);
|
|
authResolver = new VkAuthContextResolver(tokenRefresher);
|
|
|
|
process.env.VK_SERVICE_TOKEN = serviceTokenPlain;
|
|
process.env.USE_VK_MOCK = 'false';
|
|
|
|
// 1. User with valid encrypted VK credential
|
|
const encryptedAccessToken = await tokenVault.encrypt(userTokenPlain);
|
|
loggedInUser = await userRepo.upsertUserWithTokens({
|
|
vkUserId: '11112222',
|
|
firstName: 'Alice',
|
|
lastName: 'Organizer',
|
|
encryptedAccessToken,
|
|
expiresIn: 3600,
|
|
});
|
|
const sessionId1 = await defaultSessionStore.createSession(loggedInUser);
|
|
sessionCookie = `${SESSION_COOKIE_NAME}=${sessionId1}`;
|
|
|
|
// 2. User with session but NO VK credentials in repository
|
|
loggedInUserWithoutCreds = await userRepo.upsertUserWithTokens({
|
|
vkUserId: '33334444',
|
|
firstName: 'Charlie',
|
|
lastName: 'NoCreds',
|
|
encryptedAccessToken: '', // empty / missing
|
|
});
|
|
// Remove credentials for Charlie
|
|
(userRepo as any).credentials.delete(loggedInUserWithoutCreds.id);
|
|
const sessionId2 = await defaultSessionStore.createSession(loggedInUserWithoutCreds);
|
|
sessionCookieNoCreds = `${SESSION_COOKIE_NAME}=${sessionId2}`;
|
|
|
|
// Reset GiveawayStore
|
|
GiveawayStore.setRepository(new MemoryGiveawayRepository());
|
|
});
|
|
|
|
// ─── Test 1: Logged-in user + public post + SERVICE succeeds → PUBLIC_SERVICE ───
|
|
it('1. logged-in user + public post + SERVICE succeeds reports accessMode PUBLIC_SERVICE', async () => {
|
|
const mockClient: IVkClient = {
|
|
call: async (_method, _params, auth) => {
|
|
expect(auth.type).toBe('SERVICE');
|
|
return {
|
|
items: [
|
|
{
|
|
id: 101,
|
|
owner_id: -101,
|
|
date: 1700000000,
|
|
text: 'Public wall post',
|
|
likes: { count: 10 },
|
|
comments: { count: 2 },
|
|
reposts: { count: 0 },
|
|
},
|
|
],
|
|
} as any;
|
|
},
|
|
};
|
|
|
|
const provider = new VkProvider(serviceTokenPlain, mockClient, authResolver);
|
|
ProviderFactory.getVkProvider = () => provider;
|
|
|
|
const req = new NextRequest('http://localhost:3000/api/posts/preview', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Cookie': sessionCookie,
|
|
},
|
|
body: JSON.stringify({ url: 'https://vk.com/wall-101_101' }),
|
|
});
|
|
|
|
const res = await previewPost(req);
|
|
const json = await res.json();
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(json.success).toBe(true);
|
|
// Truthfulness: Despite user having a session, the post was fetched with SERVICE token
|
|
expect(json.post.resolvedAuthType).toBe('SERVICE');
|
|
expect(json.effectiveCapabilities.accessMode).toBe('PUBLIC_SERVICE');
|
|
expect(json.effectiveCapabilities.adminDetection).toBe(false);
|
|
});
|
|
|
|
// ─── Test 2: SERVICE denied + USER succeeds → ORGANIZER_USER ─────────────────
|
|
it('2. SERVICE denied (private) + USER fallback succeeds reports accessMode ORGANIZER_USER', async () => {
|
|
const authSequence: VkAuthContext[] = [];
|
|
|
|
const mockClient: IVkClient = {
|
|
call: async (_method, _params, auth) => {
|
|
authSequence.push(auth!);
|
|
if (auth.type === 'SERVICE') {
|
|
// Private post access denied on service token
|
|
throw new VkPrivateResourceError('Post is private', { errorCode: 15 });
|
|
}
|
|
// USER token succeeds
|
|
return {
|
|
items: [
|
|
{
|
|
id: 202,
|
|
owner_id: -202,
|
|
date: 1700000000,
|
|
text: 'Private group post visible to organizer',
|
|
likes: { count: 25 },
|
|
comments: { count: 7 },
|
|
reposts: { count: 0 },
|
|
},
|
|
],
|
|
} as any;
|
|
},
|
|
};
|
|
|
|
const provider = new VkProvider(serviceTokenPlain, mockClient, authResolver);
|
|
ProviderFactory.getVkProvider = () => provider;
|
|
|
|
const req = new NextRequest('http://localhost:3000/api/posts/preview', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Cookie': sessionCookie,
|
|
},
|
|
body: JSON.stringify({ url: 'https://vk.com/wall-202_202' }),
|
|
});
|
|
|
|
const res = await previewPost(req);
|
|
const json = await res.json();
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(json.success).toBe(true);
|
|
expect(authSequence).toHaveLength(2);
|
|
expect(authSequence[0].type).toBe('SERVICE');
|
|
expect(authSequence[1].type).toBe('USER');
|
|
|
|
// Truthfulness: Since USER fallback was actually used, it accurately reports ORGANIZER_USER
|
|
expect(json.post.resolvedAuthType).toBe('USER');
|
|
expect(json.effectiveCapabilities.accessMode).toBe('ORGANIZER_USER');
|
|
});
|
|
|
|
// ─── Test 3: Logged-in user with missing credentials + public SERVICE succeeds ─
|
|
it('3. logged-in user with missing USER credential + public SERVICE access does NOT claim ORGANIZER_USER', async () => {
|
|
const mockClient: IVkClient = {
|
|
call: async () => ({
|
|
items: [
|
|
{
|
|
id: 303,
|
|
owner_id: -303,
|
|
date: 1700000000,
|
|
text: 'Public post for user without VK creds',
|
|
likes: { count: 1 },
|
|
comments: { count: 0 },
|
|
reposts: { count: 0 },
|
|
},
|
|
],
|
|
} as any),
|
|
};
|
|
|
|
const provider = new VkProvider(serviceTokenPlain, mockClient, authResolver);
|
|
ProviderFactory.getVkProvider = () => provider;
|
|
|
|
const req = new NextRequest('http://localhost:3000/api/posts/preview', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Cookie': sessionCookieNoCreds, // Charlie has no VK credentials
|
|
},
|
|
body: JSON.stringify({ url: 'https://vk.com/wall-303_303' }),
|
|
});
|
|
|
|
const res = await previewPost(req);
|
|
const json = await res.json();
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(json.success).toBe(true);
|
|
// Truthfulness: must NOT claim ORGANIZER_USER
|
|
expect(json.post.resolvedAuthType).toBe('SERVICE');
|
|
expect(json.effectiveCapabilities.accessMode).toBe('PUBLIC_SERVICE');
|
|
});
|
|
|
|
// ─── Test 4: Anonymous + SERVICE succeeds → PUBLIC_SERVICE ───────────────────
|
|
it('4. anonymous request + SERVICE succeeds reports accessMode PUBLIC_SERVICE', async () => {
|
|
const mockClient: IVkClient = {
|
|
call: async () => ({
|
|
items: [
|
|
{
|
|
id: 404,
|
|
owner_id: -404,
|
|
date: 1700000000,
|
|
text: 'Anonymous preview post',
|
|
likes: { count: 50 },
|
|
comments: { count: 12 },
|
|
reposts: { count: 3 },
|
|
},
|
|
],
|
|
} as any),
|
|
};
|
|
|
|
const provider = new VkProvider(serviceTokenPlain, mockClient, authResolver);
|
|
ProviderFactory.getVkProvider = () => provider;
|
|
|
|
const req = new NextRequest('http://localhost:3000/api/posts/preview', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
// No Cookie header
|
|
},
|
|
body: JSON.stringify({ url: 'https://vk.com/wall-404_404' }),
|
|
});
|
|
|
|
const res = await previewPost(req);
|
|
const json = await res.json();
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(json.success).toBe(true);
|
|
expect(json.post.resolvedAuthType).toBe('SERVICE');
|
|
expect(json.effectiveCapabilities.accessMode).toBe('PUBLIC_SERVICE');
|
|
});
|
|
|
|
// ─── Test 5: No token/credential/auth object leaked in preview response ───────
|
|
it('5. preview response never contains tokens, credentials, or internal auth objects', async () => {
|
|
const mockClient: IVkClient = {
|
|
call: async () => ({
|
|
items: [
|
|
{
|
|
id: 505,
|
|
owner_id: -505,
|
|
date: 1700000000,
|
|
text: 'Security check post',
|
|
likes: { count: 2 },
|
|
comments: { count: 1 },
|
|
reposts: { count: 0 },
|
|
},
|
|
],
|
|
} as any),
|
|
};
|
|
|
|
const provider = new VkProvider(serviceTokenPlain, mockClient, authResolver);
|
|
ProviderFactory.getVkProvider = () => provider;
|
|
|
|
const req = new NextRequest('http://localhost:3000/api/posts/preview', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Cookie': sessionCookie,
|
|
},
|
|
body: JSON.stringify({ url: 'https://vk.com/wall-505_505' }),
|
|
});
|
|
|
|
const res = await previewPost(req);
|
|
const text = await res.text();
|
|
|
|
// Security check: raw response text must NOT leak any secret strings
|
|
expect(text).not.toContain(userTokenPlain);
|
|
expect(text).not.toContain(serviceTokenPlain);
|
|
expect(text).not.toContain('tokenVault');
|
|
expect(text).not.toContain('encryptedAccessToken');
|
|
expect(text).not.toContain('refreshToken');
|
|
|
|
const parsed = JSON.parse(text);
|
|
// Post only contains safe literal resolvedAuthType string
|
|
expect(parsed.post.resolvedAuthType).toBe('SERVICE');
|
|
expect(parsed.post).not.toHaveProperty('token');
|
|
expect(parsed.post).not.toHaveProperty('authContext');
|
|
});
|
|
|
|
// ─── Test 6: Giveaway detail effectiveCapabilities truthfulness ──────────────
|
|
it('6. giveaway detail endpoint reports ORGANIZER_USER when user has credentials, PUBLIC_SERVICE when missing', async () => {
|
|
// 6a. Create giveaway for Alice (has credentials)
|
|
const gwAlice = await GiveawayStore.create({
|
|
sourceUrl: 'https://vk.com/wall-101_101',
|
|
organizerId: loggedInUser.id,
|
|
filterRules: DEFAULT_FILTER_RULES,
|
|
post: {
|
|
platform: 'VK',
|
|
ownerId: '-101',
|
|
postId: '101',
|
|
sourceUrl: 'https://vk.com/wall-101_101',
|
|
title: 'Alice Giveaway',
|
|
text: 'Text',
|
|
likesCount: 10,
|
|
commentsCount: 2,
|
|
repostsCount: 0,
|
|
},
|
|
});
|
|
|
|
const reqAlice = new NextRequest(`http://localhost:3000/api/giveaways/${gwAlice.id}`, {
|
|
method: 'GET',
|
|
headers: { 'Cookie': sessionCookie },
|
|
});
|
|
|
|
const resAlice = await giveawayDetailGet(reqAlice, { params: { id: gwAlice.id } });
|
|
const jsonAlice = await resAlice.json();
|
|
|
|
expect(resAlice.status).toBe(200);
|
|
expect(jsonAlice.effectiveCapabilities.accessMode).toBe('ORGANIZER_USER');
|
|
expect(jsonAlice.effectiveCapabilities.credentialStatus).toBe('AVAILABLE');
|
|
|
|
// 6b. Create giveaway for Charlie (no credentials)
|
|
const gwCharlie = await GiveawayStore.create({
|
|
sourceUrl: 'https://vk.com/wall-303_303',
|
|
organizerId: loggedInUserWithoutCreds.id,
|
|
filterRules: DEFAULT_FILTER_RULES,
|
|
post: {
|
|
platform: 'VK',
|
|
ownerId: '-303',
|
|
postId: '303',
|
|
sourceUrl: 'https://vk.com/wall-303_303',
|
|
title: 'Charlie Giveaway',
|
|
text: 'Text',
|
|
likesCount: 1,
|
|
commentsCount: 0,
|
|
repostsCount: 0,
|
|
},
|
|
});
|
|
|
|
const reqCharlie = new NextRequest(`http://localhost:3000/api/giveaways/${gwCharlie.id}`, {
|
|
method: 'GET',
|
|
headers: { 'Cookie': sessionCookieNoCreds },
|
|
});
|
|
|
|
const resCharlie = await giveawayDetailGet(reqCharlie, { params: { id: gwCharlie.id } });
|
|
const jsonCharlie = await resCharlie.json();
|
|
|
|
expect(resCharlie.status).toBe(200);
|
|
expect(jsonCharlie.effectiveCapabilities.accessMode).toBe('PUBLIC_SERVICE');
|
|
expect(jsonCharlie.effectiveCapabilities.credentialStatus).toBe('MISSING');
|
|
});
|
|
|
|
// ─── Test 7: Expired credential + refresh token available → ORGANIZER_USER (REFRESHABLE) ─
|
|
it('7. expired credential + refresh token reports ORGANIZER_USER with REFRESHABLE status', async () => {
|
|
const encryptedAccessToken = await tokenVault.encrypt('old_token');
|
|
const encryptedRefreshToken = await tokenVault.encrypt('valid_refresh_token');
|
|
|
|
const expiredUser = await userRepo.upsertUserWithTokens({
|
|
vkUserId: '55556666',
|
|
firstName: 'David',
|
|
lastName: 'Refreshable',
|
|
encryptedAccessToken,
|
|
encryptedRefreshToken,
|
|
expiresIn: -60, // expired 1 minute ago
|
|
});
|
|
|
|
const sessionId = await defaultSessionStore.createSession(expiredUser);
|
|
const cookie = `${SESSION_COOKIE_NAME}=${sessionId}`;
|
|
|
|
const gw = await GiveawayStore.create({
|
|
sourceUrl: 'https://vk.com/wall-555_555',
|
|
organizerId: expiredUser.id,
|
|
filterRules: DEFAULT_FILTER_RULES,
|
|
post: {
|
|
platform: 'VK',
|
|
ownerId: '-555',
|
|
postId: '555',
|
|
sourceUrl: 'https://vk.com/wall-555_555',
|
|
title: 'David Giveaway',
|
|
text: 'Text',
|
|
likesCount: 5,
|
|
commentsCount: 1,
|
|
repostsCount: 0,
|
|
},
|
|
});
|
|
|
|
const req = new NextRequest(`http://localhost:3000/api/giveaways/${gw.id}`, {
|
|
method: 'GET',
|
|
headers: { 'Cookie': cookie },
|
|
});
|
|
|
|
const res = await giveawayDetailGet(req, { params: { id: gw.id } });
|
|
const json = await res.json();
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(json.effectiveCapabilities.accessMode).toBe('ORGANIZER_USER');
|
|
expect(json.effectiveCapabilities.credentialStatus).toBe('REFRESHABLE');
|
|
});
|
|
|
|
// ─── Test 8: Expired credential WITHOUT refresh token → PUBLIC_SERVICE (REAUTH_REQUIRED) ─
|
|
it('8. expired credential without refresh token reports PUBLIC_SERVICE with REAUTH_REQUIRED status', async () => {
|
|
const encryptedAccessToken = await tokenVault.encrypt('old_token_no_refresh');
|
|
|
|
const expiredNoRefreshUser = await userRepo.upsertUserWithTokens({
|
|
vkUserId: '77779999',
|
|
firstName: 'Eve',
|
|
lastName: 'NoRefresh',
|
|
encryptedAccessToken,
|
|
expiresIn: -60, // expired 1 minute ago
|
|
});
|
|
|
|
const sessionId = await defaultSessionStore.createSession(expiredNoRefreshUser);
|
|
const cookie = `${SESSION_COOKIE_NAME}=${sessionId}`;
|
|
|
|
const gw = await GiveawayStore.create({
|
|
sourceUrl: 'https://vk.com/wall-777_777',
|
|
organizerId: expiredNoRefreshUser.id,
|
|
filterRules: DEFAULT_FILTER_RULES,
|
|
post: {
|
|
platform: 'VK',
|
|
ownerId: '-777',
|
|
postId: '777',
|
|
sourceUrl: 'https://vk.com/wall-777_777',
|
|
title: 'Eve Giveaway',
|
|
text: 'Text',
|
|
likesCount: 5,
|
|
commentsCount: 1,
|
|
repostsCount: 0,
|
|
},
|
|
});
|
|
|
|
const req = new NextRequest(`http://localhost:3000/api/giveaways/${gw.id}`, {
|
|
method: 'GET',
|
|
headers: { 'Cookie': cookie },
|
|
});
|
|
|
|
const res = await giveawayDetailGet(req, { params: { id: gw.id } });
|
|
const json = await res.json();
|
|
|
|
expect(res.status).toBe(200);
|
|
// Must NOT claim ORGANIZER_USER
|
|
expect(json.effectiveCapabilities.accessMode).toBe('PUBLIC_SERVICE');
|
|
expect(json.effectiveCapabilities.credentialStatus).toBe('REAUTH_REQUIRED');
|
|
});
|
|
|
|
// ─── Test 9: Null expiresAt WITHOUT refresh token → PUBLIC_SERVICE (REAUTH_REQUIRED) ─
|
|
it('9. null expiresAt without refresh token reports PUBLIC_SERVICE with REAUTH_REQUIRED status', async () => {
|
|
const encryptedAccessToken = await tokenVault.encrypt('legacy_token');
|
|
|
|
const legacyUser = await userRepo.upsertUserWithTokens({
|
|
vkUserId: '88880000',
|
|
firstName: 'Frank',
|
|
lastName: 'LegacyNoExpiry',
|
|
encryptedAccessToken,
|
|
// expiresIn: undefined -> expiresAt: null
|
|
});
|
|
|
|
const sessionId = await defaultSessionStore.createSession(legacyUser);
|
|
const cookie = `${SESSION_COOKIE_NAME}=${sessionId}`;
|
|
|
|
const gw = await GiveawayStore.create({
|
|
sourceUrl: 'https://vk.com/wall-888_888',
|
|
organizerId: legacyUser.id,
|
|
filterRules: DEFAULT_FILTER_RULES,
|
|
post: {
|
|
platform: 'VK',
|
|
ownerId: '-888',
|
|
postId: '888',
|
|
sourceUrl: 'https://vk.com/wall-888_888',
|
|
title: 'Frank Giveaway',
|
|
text: 'Text',
|
|
likesCount: 5,
|
|
commentsCount: 1,
|
|
repostsCount: 0,
|
|
},
|
|
});
|
|
|
|
const req = new NextRequest(`http://localhost:3000/api/giveaways/${gw.id}`, {
|
|
method: 'GET',
|
|
headers: { 'Cookie': cookie },
|
|
});
|
|
|
|
const res = await giveawayDetailGet(req, { params: { id: gw.id } });
|
|
const json = await res.json();
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(json.effectiveCapabilities.accessMode).toBe('PUBLIC_SERVICE');
|
|
expect(json.effectiveCapabilities.credentialStatus).toBe('REAUTH_REQUIRED');
|
|
});
|
|
|
|
// ─── Test 10: Null expiresAt WITH refresh token → ORGANIZER_USER (REFRESHABLE) ─
|
|
it('10. null expiresAt with refresh token reports ORGANIZER_USER with REFRESHABLE status', async () => {
|
|
const encryptedAccessToken = await tokenVault.encrypt('legacy_token');
|
|
const encryptedRefreshToken = await tokenVault.encrypt('valid_refresh_token');
|
|
|
|
const legacyRefreshUser = await userRepo.upsertUserWithTokens({
|
|
vkUserId: '99991111',
|
|
firstName: 'Grace',
|
|
lastName: 'LegacyRefreshable',
|
|
encryptedAccessToken,
|
|
encryptedRefreshToken,
|
|
// expiresIn: undefined -> expiresAt: null
|
|
});
|
|
|
|
const sessionId = await defaultSessionStore.createSession(legacyRefreshUser);
|
|
const cookie = `${SESSION_COOKIE_NAME}=${sessionId}`;
|
|
|
|
const gw = await GiveawayStore.create({
|
|
sourceUrl: 'https://vk.com/wall-999_999',
|
|
organizerId: legacyRefreshUser.id,
|
|
filterRules: DEFAULT_FILTER_RULES,
|
|
post: {
|
|
platform: 'VK',
|
|
ownerId: '-999',
|
|
postId: '999',
|
|
sourceUrl: 'https://vk.com/wall-999_999',
|
|
title: 'Grace Giveaway',
|
|
text: 'Text',
|
|
likesCount: 5,
|
|
commentsCount: 1,
|
|
repostsCount: 0,
|
|
},
|
|
});
|
|
|
|
const req = new NextRequest(`http://localhost:3000/api/giveaways/${gw.id}`, {
|
|
method: 'GET',
|
|
headers: { 'Cookie': cookie },
|
|
});
|
|
|
|
const res = await giveawayDetailGet(req, { params: { id: gw.id } });
|
|
const json = await res.json();
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(json.effectiveCapabilities.accessMode).toBe('ORGANIZER_USER');
|
|
expect(json.effectiveCapabilities.credentialStatus).toBe('REFRESHABLE');
|
|
});
|
|
|
|
// ─── Test 11: Giveaway detail does not leak tokens or secret fields ──────────
|
|
it('11. giveaway detail response never leaks token or secret fields', async () => {
|
|
const gwAlice = await GiveawayStore.create({
|
|
sourceUrl: 'https://vk.com/wall-101_101',
|
|
organizerId: loggedInUser.id,
|
|
filterRules: DEFAULT_FILTER_RULES,
|
|
post: {
|
|
platform: 'VK',
|
|
ownerId: '-101',
|
|
postId: '101',
|
|
sourceUrl: 'https://vk.com/wall-101_101',
|
|
title: 'Alice Giveaway',
|
|
text: 'Text',
|
|
likesCount: 10,
|
|
commentsCount: 2,
|
|
repostsCount: 0,
|
|
},
|
|
});
|
|
|
|
const reqAlice = new NextRequest(`http://localhost:3000/api/giveaways/${gwAlice.id}`, {
|
|
method: 'GET',
|
|
headers: { 'Cookie': sessionCookie },
|
|
});
|
|
|
|
const resAlice = await giveawayDetailGet(reqAlice, { params: { id: gwAlice.id } });
|
|
const rawText = await resAlice.text();
|
|
|
|
expect(rawText).not.toContain(userTokenPlain);
|
|
expect(rawText).not.toContain(serviceTokenPlain);
|
|
expect(rawText).not.toContain('encryptedAccessToken');
|
|
expect(rawText).not.toContain('encryptedRefreshToken');
|
|
expect(rawText).not.toContain('tokenVault');
|
|
});
|
|
});
|