Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import type { Connections, Article, TopicChannel } from '#definitions/index.js'

import { FEATURE_FLAG, FEATURE_NAME } from '#common/enums/index.js'
import { AtomService } from '../../atomService.js'
import { CampaignService } from '../../campaignService.js'
import { ChannelService } from '../../channel/channelService.js'
import { SystemService } from '../../systemService.js'
import { genConnections, closeConnections, createCampaign } from '../utils.js'

let connections: Connections
Expand All @@ -11,12 +13,14 @@ let atomService: AtomService
let channel: TopicChannel
let articles: Article[]
let campaignService: CampaignService
let systemService: SystemService

beforeAll(async () => {
connections = await genConnections()
channelService = new ChannelService(connections)
atomService = new AtomService(connections)
campaignService = new CampaignService(connections)
systemService = new SystemService(connections)
}, 30000)

afterAll(async () => {
Expand All @@ -40,6 +44,20 @@ beforeEach(async () => {
take: 6,
})
expect(articles).toHaveLength(6)
await Promise.all(
articles.map((article) =>
atomService.update({
table: 'article',
where: { id: article.id },
data: { isSpam: null, spamScore: null },
})
)
)
await systemService.setFeatureFlag({
name: FEATURE_NAME.spam_detection,
flag: FEATURE_FLAG.on,
value: 0.5,
})

// Add articles to channel
await channelService.setArticleTopicChannels({
Expand Down Expand Up @@ -180,6 +198,33 @@ describe('findTopicChannelArticles', () => {
}
})

test('excludes unpinned articles with spam score above threshold', async () => {
await atomService.update({
table: 'article',
where: { id: articles[0].id },
data: { isSpam: null, spamScore: 0.6 },
})
await atomService.update({
table: 'article',
where: { id: articles[1].id },
data: { isSpam: false, spamScore: 0.9 },
})
await atomService.update({
table: 'article',
where: { id: articles[2].id },
data: { isSpam: true, spamScore: 0.1 },
})

const { query } = await channelService.findTopicChannelArticles(channel.id)
const results = await query
const resultIds = results.map((a) => a.id)

expect(resultIds).not.toContain(articles[0].id)
expect(resultIds).toContain(articles[1].id)
expect(resultIds).not.toContain(articles[2].id)
expect(resultIds).toContain(articles[3].id)
})

describe('datetimeRange filtering', () => {
const baseTime = new Date('2024-01-01T00:00:00Z')
const oneDayBefore = new Date(baseTime.getTime() - 86400000)
Expand Down
16 changes: 9 additions & 7 deletions src/connectors/__test__/channelService/topicChannel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ describe('setArticleChannels', () => {
beforeEach(async () => {
await atomService.deleteMany({ table: 'topic_channel_article' })
await atomService.deleteMany({ table: 'topic_channel' })
await atomService.update({
table: 'article',
where: { id: articleId },
data: { isSpam: null, spamScore: null },
})
})

test('sets article channels', async () => {
Expand Down Expand Up @@ -215,8 +220,7 @@ describe('setArticleChannels', () => {
expect(articleChannels[1].isLabeled).toBe(true)
})

test('sets isSpam to false when adding articles to channels', async () => {
// First set article as spam
test('does not clear isSpam when adding articles to channels', async () => {
await atomService.update({
table: 'article',
where: { id: articleId },
Expand All @@ -229,15 +233,14 @@ describe('setArticleChannels', () => {
channelIds: [channel.id],
})

// Verify article is no longer marked as spam
const article = await atomService.findUnique({
table: 'article',
where: { id: articleId },
})
expect(article.isSpam).toBe(false)
expect(article.isSpam).toBe(true)
})

test('sets isSpam to false when re-adding articles to channels', async () => {
test('does not clear isSpam when re-adding articles to channels', async () => {
const channel = await channelService.createTopicChannel(channelData)

// First add article to channel
Expand Down Expand Up @@ -265,12 +268,11 @@ describe('setArticleChannels', () => {
channelIds: [channel.id],
})

// Verify article is no longer marked as spam
const article = await atomService.findUnique({
table: 'article',
where: { id: articleId },
})
expect(article.isSpam).toBe(false)
expect(article.isSpam).toBe(true)
})

test('removes existing channels when setting empty array', async () => {
Expand Down
15 changes: 5 additions & 10 deletions src/connectors/channel/channelService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { ArticleService } from '../article/articleService.js'
import { AtomService } from '../atomService.js'
import { Cache } from '../cache/index.js'
import { NotificationService } from '../notification/notificationService.js'
import { SystemService } from '../systemService.js'

import { ChannelClassifier } from './channelClassifier.js'

Expand Down Expand Up @@ -321,11 +322,6 @@ export class ChannelService {

// Add new channels or re-enable disabled ones
if (toAdd.length > 0) {
await this.models.update({
table: 'article',
where: { id: articleId },
data: { isSpam: false },
})
await this.models.upsertOnConflict({
table: 'topic_channel_article',
create: toAdd.map((channelId) =>
Expand Down Expand Up @@ -397,6 +393,9 @@ export class ChannelService {
}

const pinnedArticleIds = channel.pinnedArticles || []
const spamThreshold = await new SystemService(
this.connections
).getSpamThreshold()

const pinnedQuery = knexRO
.select(
Expand Down Expand Up @@ -426,11 +425,7 @@ export class ChannelService {
'article.channel_enabled': true,
})
.whereIn('topic_channel_article.channel_id', channelIds)
.where((qb) => {
qb.where('article.is_spam', false).orWhere((b) => {
b.whereNull('article.is_spam')
})
})
.modify(excludeSpamModifier, spamThreshold)
.modify(excludeRestrictedModifier)
.modify(excludeExclusiveCampaignArticles)
.where((builder) => {
Expand Down
Loading