Skip to content

Commit

Permalink
Merge branch 'main' into feat-share-post-flow
Browse files Browse the repository at this point in the history
  • Loading branch information
rebelchris authored Jan 29, 2025
2 parents b421367 + ab1a211 commit badbe41
Show file tree
Hide file tree
Showing 31 changed files with 510 additions and 97 deletions.
6 changes: 5 additions & 1 deletion .infra/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ export const workers: Worker[] = [
topic: 'user-updated',
subscription: 'api.user-updated-plus-subscribed-custom-feed',
},
{
topic: 'user-updated',
subscription: 'api.user-gifted-plus-notification',
},
{
topic: 'user-deleted',
subscription: 'api.user-deleted-cio',
Expand Down Expand Up @@ -328,7 +332,7 @@ export const workers: Worker[] = [
{
topic: 'kvasir.v1.post-translated',
subscription: 'api.post-translated',
},
}
];

export const personalizedDigestWorkers: Worker[] = [
Expand Down
26 changes: 25 additions & 1 deletion __tests__/boot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ import { getEncryptedFeatures } from '../src/growthbook';
import { base64 } from 'graphql-relay/utils/base64';
import { cookies } from '../src/cookies';
import { signJwt } from '../src/auth';
import { DEFAULT_TIMEZONE, submitArticleThreshold } from '../src/common';
import {
DEFAULT_TIMEZONE,
submitArticleThreshold,
updateFlagsStatement,
} from '../src/common';
import { saveReturnAlerts } from '../src/schema/alerts';
import { UserVote } from '../src/types';
import { BootAlerts, excludeProperties } from '../src/routes/boot';
Expand Down Expand Up @@ -92,6 +96,7 @@ const LOGGED_IN_BODY = {
alerts: {
...BASE_BODY.alerts,
bootPopup: true,
shouldShowGiftPlus: false,
},
accessToken: {
expiresIn: expect.any(String),
Expand Down Expand Up @@ -149,6 +154,7 @@ const getBootAlert = (data: Alerts): BootAlerts =>
]),
shouldShowFeedFeedback:
subDays(new Date(), FEED_SURVEY_INTERVAL) > data.lastFeedSettingsFeedback,
shouldShowGiftPlus: false,
}) as BootAlerts;

beforeAll(async () => {
Expand Down Expand Up @@ -866,6 +872,24 @@ describe('boot alerts', () => {
.expect(200);
expect(res.body.alerts).toEqual(alerts);
});

it('should return shouldShowGiftPlus if user is gift recipient', async () => {
mockLoggedIn();
await con.getRepository(User).update(
{ id: '1' },
{
flags: updateFlagsStatement({
showPlusGift: true,
}),
},
);

const res = await request(app.server)
.get(BASE_PATH)
.set('Cookie', 'ory_kratos_session=value;')
.expect(200);
expect(res.body.alerts.shouldShowGiftPlus).toEqual(true);
});
});

describe('boot misc', () => {
Expand Down
67 changes: 31 additions & 36 deletions __tests__/common/post.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { getPostSmartTitle, getPostTranslatedTitle } from '../../src/common';
import type { Post } from '../../src/entity';
import { ContentLanguage } from '../../src/types';

describe('getPostTranslatedTitle', () => {
it('should return the translated title if it exists', () => {
const post = {
const post: Partial<Post> = {
title: 'Original Title',
contentMeta: {
translate_title: {
translations: {
[ContentLanguage.German]: 'Übersetzter Titel',
},
translation: {
[ContentLanguage.German]: {
title: 'Übersetzter Titel',
},
},
};
Expand All @@ -19,13 +18,11 @@ describe('getPostTranslatedTitle', () => {
});

it('should return the original title if the translation does not exist', () => {
const post = {
const post: Partial<Post> = {
title: 'Original Title',
contentMeta: {
translate_title: {
translations: {
[ContentLanguage.Spanish]: 'Título Traducido',
},
translation: {
[ContentLanguage.Spanish]: {
title: 'Título Traducido',
},
},
};
Expand All @@ -34,34 +31,34 @@ describe('getPostTranslatedTitle', () => {
expect(result).toBe('Original Title');
});

it('should return the original title if contentMeta is undefined', () => {
const post = {
it('should return the original title if translation is undefined', () => {
const post: Partial<Post> = {
title: 'Original Title',
contentMeta: undefined,
translation: undefined,
};

const result = getPostTranslatedTitle(post, ContentLanguage.German);
expect(result).toBe('Original Title');
});

it('should return the original title if translate_title is undefined', () => {
const post = {
it('should return the original title if "de" is undefined', () => {
const post: Partial<Post> = {
title: 'Original Title',
contentMeta: {
translate_title: undefined,
translation: {
[ContentLanguage.German]: undefined,
},
};

const result = getPostTranslatedTitle(post, ContentLanguage.German);
expect(result).toBe('Original Title');
});

it('should return the original title if translations is undefined', () => {
const post = {
it('should return the original title if title is undefined', () => {
const post: Partial<Post> = {
title: 'Original Title',
contentMeta: {
translate_title: {
translations: undefined,
translation: {
[ContentLanguage.German]: {
title: undefined,
},
},
};
Expand All @@ -73,39 +70,39 @@ describe('getPostTranslatedTitle', () => {

describe('getPostSmartTitle', () => {
it('should return the alt title for the specified content language', () => {
const post = {
const post: Partial<Post> = {
title: 'Default Title',
contentMeta: {
alt_title: {
translations: {
[ContentLanguage.Spanish]: 'Título en Español',
},
},
},
title: 'Default Title',
};

const result = getPostSmartTitle(post, ContentLanguage.Spanish);
expect(result).toBe('Título en Español');
});

it('should return the English alt title if specified content language is not available', () => {
const post = {
const post: Partial<Post> = {
title: 'Default Title',
contentMeta: {
alt_title: {
translations: {
[ContentLanguage.English]: 'Title in English',
},
},
},
title: 'Default Title',
};

const result = getPostSmartTitle(post, ContentLanguage.Spanish);
expect(result).toBe('Title in English');
});

it('should return the default title if no alt title translations are available', () => {
const post = {
const post: Partial<Post> = {
title: 'Default Title',
};

Expand All @@ -114,7 +111,7 @@ describe('getPostSmartTitle', () => {
});

it('should return the default title if contentMeta is not defined', () => {
const post = {
const post: Partial<Post> = {
title: 'Default Title',
};

Expand All @@ -123,13 +120,11 @@ describe('getPostSmartTitle', () => {
});

it('should return the translated title if no alt title translations are available', () => {
const post = {
const post: Partial<Post> = {
title: 'Default Title',
contentMeta: {
translate_title: {
translations: {
[ContentLanguage.German]: 'Übersetzter Titel',
},
translation: {
[ContentLanguage.German]: {
title: 'Übersetzter Titel',
},
},
};
Expand Down
33 changes: 33 additions & 0 deletions __tests__/cron/cleanGiftedPlus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import createOrGetConnection from '../../src/db';
import { addDays, subDays } from 'date-fns';
import { SubscriptionCycles } from '../../src/paddle';
import { crons } from '../../src/cron/index';
import { updateFlagsStatement } from '../../src/common';

let con: DataSource;

Expand Down Expand Up @@ -83,4 +84,36 @@ describe('cleanGiftedPlus cron', () => {
expect(users[0].id).toEqual('2-clgp');
expect(users[1].id).toEqual('5-clgp');
});

it('should remove user flag on expire', async () => {
const yesterdayDate = subDays(new Date(), 1);
// 1 is gifted and expired
await con.getRepository(User).update(
{ id: '1-clgp' },
{
subscriptionFlags: {
giftExpirationDate: yesterdayDate,
gifterId: '2-clgp',
cycle: SubscriptionCycles.Yearly,
},
flags: updateFlagsStatement({
vordr: false,
trustScore: 1,
showPlusGift: true,
}),
},
);

await expectSuccessfulCron(cron);

const user = await con.getRepository(User).findOneByOrFail({
id: '1-clgp',
});

expect(user.flags).toStrictEqual({
vordr: false,
trustScore: 1,
showPlusGift: false,
});
});
});
28 changes: 27 additions & 1 deletion __tests__/notifications/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
NotificationCommentContext,
NotificationCommenterContext,
NotificationDoneByContext,
NotificationGiftPlusContext,
NotificationPostContext,
NotificationPostModerationContext,
NotificationSourceContext,
Expand All @@ -15,9 +16,9 @@ import {
NotificationSubmissionContext,
NotificationUpvotersContext,
NotificationUserContext,
type NotificationUserTopReaderContext,
Reference,
storeNotificationBundleV2,
type NotificationUserTopReaderContext,
} from '../../src/notifications';
import { postsFixture } from '../fixture/post';
import {
Expand Down Expand Up @@ -1513,3 +1514,28 @@ describe('storeNotificationBundle', () => {
expect(actual.attachments!.length).toEqual(0);
});
});

describe('plus notifications', () => {
it('should notify user when they are gifted a subscription', async () => {
const type = NotificationType.UserGiftedPlus;
const gifter = usersFixture[1] as Reference<User>;
const recipient = usersFixture[0] as Reference<User>;
const squad = sourcesFixture[5] as Reference<SquadSource>;
const ctx: NotificationGiftPlusContext = {
userIds: [recipient.id],
gifter,
recipient,
squad,
};
const actual = generateNotificationV2(type, ctx);

expect(actual.notification.type).toEqual(type);
expect(actual.userIds).toEqual([recipient.id]);
expect(actual.notification.public).toEqual(true);
expect(actual.notification.referenceId).toBe('squad');
expect(actual.notification.description).toBeFalsy();
expect(actual.notification.targetUrl).toContain(`/squads/${squad.handle}`);
expect(actual.avatars?.[0]?.image).toEqual(gifter.image);
expect(actual.attachments!.length).toEqual(0);
});
});
32 changes: 32 additions & 0 deletions __tests__/paddle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ beforeEach(async () => {
[...usersFixture, ...plusUsersFixture].map((user) => ({
...user,
id: `whp-${user.id}`,
flags: {
vordr: false,
trustScore: 1,
},
})),
);
});
Expand Down Expand Up @@ -174,4 +178,32 @@ describe('gift', () => {
expect(expireDate).toBeInstanceOf(Date);
expect(expireDate?.getFullYear()).toBe(new Date().getFullYear() + 1);
});

it('should add showPlusGift flags to recipient when gifted', async () => {
const userId = 'whp-1';
const user = await con.getRepository(User).findOneByOrFail({ id: 'whp-1' });

expect(isPlusMember(user.subscriptionFlags?.cycle)).toBe(false);
expect(user.flags).toStrictEqual({
vordr: false,
trustScore: 1,
});

const data = getSubscriptionData({
user_id: userId,
gifter_id: 'whp-2',
});
await updateUserSubscription({ data, state: true });

const updatedUser = await con
.getRepository(User)
.findOneByOrFail({ id: userId });

expect(isPlusMember(updatedUser.subscriptionFlags?.cycle)).toBe(true);
expect(updatedUser.flags).toStrictEqual({
vordr: false,
trustScore: 1,
showPlusGift: true,
});
});
});
Loading

0 comments on commit badbe41

Please sign in to comment.