fix tests part 1

This commit is contained in:
das 2025-10-17 12:07:41 +02:00
parent f3308a062d
commit d4de59d7ce
5 changed files with 51 additions and 116 deletions

View file

@ -5,6 +5,7 @@ import { FolderSystem } from "../../../common/api/common/mail/FolderSystem.js"
import { import {
assertNotNull, assertNotNull,
collectToMap, collectToMap,
downcast,
getFirstOrThrow, getFirstOrThrow,
groupBy, groupBy,
groupByAndMap, groupByAndMap,
@ -233,13 +234,12 @@ export class MailModel {
return mailboxDetail && this.inboxRuleHandler()?.findAndApplyMatchingRule(mailboxDetail, mail, isLeaderClient) return mailboxDetail && this.inboxRuleHandler()?.findAndApplyMatchingRule(mailboxDetail, mail, isLeaderClient)
}) })
if (!isWebClient()) { if (isWebClient()) {
const mailDetails = await this.mailFacade.loadMailDetailsBlob(mail)
this.spamHandler().storeTrainingDatum(mail, mailDetails)
} else {
// we only need to show notifications explicitly on the webapp // we only need to show notifications explicitly on the webapp
this._showNotification(isRuleTargetFolder ?? sourceMailFolder, mail) this._showNotification(isRuleTargetFolder ?? sourceMailFolder, mail)
} } else {
const mailDetails = await this.mailFacade.loadMailDetailsBlob(mail)
this.spamHandler().storeTrainingDatum(mail, mailDetails)
if (isRuleTargetFolder) { if (isRuleTargetFolder) {
return { processingDone: Promise.resolve() } return { processingDone: Promise.resolve() }
@ -249,8 +249,9 @@ export class MailModel {
) { ) {
const folderSystem = this.getFolderSystemByGroupId(assertNotNull(mail._ownerGroup)) const folderSystem = this.getFolderSystemByGroupId(assertNotNull(mail._ownerGroup))
if (sourceMailFolder && folderSystem) { if (sourceMailFolder && folderSystem) {
const predictPromise = this.spamHandler().predictSpamForNewMail(mail, sourceMailFolder, folderSystem) const predictPromise = this.spamHandler().predictSpamForNewMail(mail, mailDetails, sourceMailFolder, folderSystem)
return { processingDone: predictPromise } return { processingDone: downcast(predictPromise) }
}
} }
} }
} }

View file

@ -60,9 +60,7 @@ export class SpamClassificationHandler {
} }
} }
public async predictSpamForNewMail(mail: Mail, folder: MailFolder, folderSystem: FolderSystem): Promise<void> { public async predictSpamForNewMail(mail: Mail, mailDetails: MailDetails, sourceFolder: MailFolder, folderSystem: FolderSystem): Promise<MailFolder> {
const mailDetails = await this.mailFacade.loadMailDetailsBlob(mail)
const spamPredMailDatum: SpamPredMailDatum = { const spamPredMailDatum: SpamPredMailDatum = {
subject: mail.subject, subject: mail.subject,
body: getMailBodyText(mailDetails.body), body: getMailBodyText(mailDetails.body),
@ -70,7 +68,7 @@ export class SpamClassificationHandler {
} }
const isSpam = (await this.spamClassifier?.predict(spamPredMailDatum)) ?? null const isSpam = (await this.spamClassifier?.predict(spamPredMailDatum)) ?? null
if (isSpam && folder.folderType === MailSetKind.INBOX) { if (isSpam && sourceFolder.folderType === MailSetKind.INBOX) {
const spamFolder = assertNotNull(folderSystem.getSystemFolderByType(MailSetKind.SPAM)) const spamFolder = assertNotNull(folderSystem.getSystemFolderByType(MailSetKind.SPAM))
if (this.spamMoveMailData) { if (this.spamMoveMailData) {
this.spamMoveMailData.mails.push(mail._id) this.spamMoveMailData.mails.push(mail._id)
@ -83,7 +81,8 @@ export class SpamClassificationHandler {
}) })
} }
this.sendMoveMailServiceRequest(this.mailFacade) this.sendMoveMailServiceRequest(this.mailFacade)
} else if (!isSpam && folder.folderType === MailSetKind.SPAM) { return spamFolder
} else if (!isSpam && sourceFolder.folderType === MailSetKind.SPAM) {
const hamFolder = assertNotNull(folderSystem.getSystemFolderByType(MailSetKind.INBOX)) const hamFolder = assertNotNull(folderSystem.getSystemFolderByType(MailSetKind.INBOX))
if (this.hamMoveMailData) { if (this.hamMoveMailData) {
this.hamMoveMailData.mails.push(mail._id) this.hamMoveMailData.mails.push(mail._id)
@ -96,9 +95,11 @@ export class SpamClassificationHandler {
}) })
} }
this.sendMoveMailServiceRequest(this.mailFacade) this.sendMoveMailServiceRequest(this.mailFacade)
return hamFolder
} else { } else {
this.classifierResultServiceMailIds.push(mail._id) this.classifierResultServiceMailIds.push(mail._id)
this.sendClassifierResultServiceRequest(this.mailFacade) this.sendClassifierResultServiceRequest(this.mailFacade)
return sourceFolder
} }
} }

View file

@ -10,6 +10,7 @@ import {
GroupType, GroupType,
PermissionType, PermissionType,
PresentableKeyVerificationState, PresentableKeyVerificationState,
ProcessingState,
PublicKeyIdentifierType, PublicKeyIdentifierType,
} from "../../../../../src/common/api/common/TutanotaConstants.js" } from "../../../../../src/common/api/common/TutanotaConstants.js"
import { import {
@ -1854,7 +1855,7 @@ o.spec("CryptoFacadeTest", function () {
mailDetailsDraft: null, mailDetailsDraft: null,
sets: [], sets: [],
keyVerificationState: null, keyVerificationState: null,
isInboxRuleApplied: true, processingState: ProcessingState.INBOX_RULE_APPLIED,
clientSpamClassifierResult: null, clientSpamClassifierResult: null,
}) })

View file

@ -1,15 +1,15 @@
import o from "@tutao/otest" import o from "@tutao/otest"
import { Notifications } from "../../../src/common/gui/Notifications.js" import { Notifications } from "../../../src/common/gui/Notifications.js"
import { mock, Spy, spy, verify } from "@tutao/tutanota-test-utils" import { mock, Spy, spy, verify } from "@tutao/tutanota-test-utils"
import { MailSetKind, OperationType } from "../../../src/common/api/common/TutanotaConstants.js" import { MailSetKind, OperationType, ProcessingState } from "../../../src/common/api/common/TutanotaConstants.js"
import { import {
BodyTypeRef, BodyTypeRef,
Mail, Mail,
MailDetails, MailDetails,
MailDetailsBlob,
MailDetailsBlobTypeRef, MailDetailsBlobTypeRef,
MailDetailsTypeRef, MailDetailsTypeRef,
MailFolderTypeRef, MailFolderTypeRef,
MailSetEntryTypeRef,
MailTypeRef, MailTypeRef,
} from "../../../src/common/api/entities/tutanota/TypeRefs.js" } from "../../../src/common/api/entities/tutanota/TypeRefs.js"
import { EntityClient } from "../../../src/common/api/common/EntityClient.js" import { EntityClient } from "../../../src/common/api/common/EntityClient.js"
@ -84,29 +84,29 @@ o.spec("MailModelTest", function () {
}) })
o("doesn't send notification for another folder", async function () { o("doesn't send notification for another folder", async function () {
const mailSetEntry = createTestEntity(MailSetEntryTypeRef, { _id: [anotherFolder.entries, "mailSetEntryId"] }) const mail = createTestEntity(MailTypeRef, { _id: ["mailBagListId", "mailId"], sets: [] })
restClient.addListInstances(mailSetEntry) restClient.addListInstances(mail)
await model.entityEventsReceived([ await model.entityEventsReceived([
makeUpdate({ makeUpdate({
instanceListId: getListId(mailSetEntry) as NonEmptyString, instanceListId: getListId(mail) as NonEmptyString,
instanceId: getElementId(mailSetEntry), instanceId: getElementId(mail),
operation: OperationType.CREATE, operation: OperationType.CREATE,
}), }),
]) ])
o(showSpy.invocations.length).equals(0) o(showSpy.invocations.length).equals(0)
}) })
o("doesn't send notification for move operation", async function () { o("doesn't send notification for move operation", async function () {
const mailSetEntry = createTestEntity(MailSetEntryTypeRef, { _id: [inboxFolder.entries, "mailSetEntryId"] }) const mail = createTestEntity(MailTypeRef, { _id: ["mailBagListId", "mailId"], sets: [] })
restClient.addListInstances(mailSetEntry) restClient.addListInstances(mail)
await model.entityEventsReceived([ await model.entityEventsReceived([
makeUpdate({ makeUpdate({
instanceListId: getListId(mailSetEntry) as NonEmptyString, instanceListId: getListId(mail) as NonEmptyString,
instanceId: getElementId(mailSetEntry), instanceId: getElementId(mail),
operation: OperationType.DELETE, operation: OperationType.DELETE,
}), }),
makeUpdate({ makeUpdate({
instanceListId: getListId(mailSetEntry) as NonEmptyString, instanceListId: getListId(mail) as NonEmptyString,
instanceId: getElementId(mailSetEntry), instanceId: getElementId(mail),
operation: OperationType.CREATE, operation: OperationType.CREATE,
}), }),
]) ])
@ -145,14 +145,18 @@ o.spec("MailModelTest", function () {
_ownerGroup: "mailGroup", _ownerGroup: "mailGroup",
mailDetails: ["detailsList", mailDetails._id], mailDetails: ["detailsList", mailDetails._id],
sets: [inboxFolder._id], sets: [inboxFolder._id],
processingState: ProcessingState.INBOX_RULE_NOT_PROCESSED,
}) })
const mailDetailsBlob = createTestEntity(MailDetailsBlobTypeRef, { const mailDetailsBlob: MailDetailsBlob = createTestEntity(MailDetailsBlobTypeRef, {
_id: mail.mailDetails!, _id: mail.mailDetails!,
details: mailDetails, details: mailDetails,
}) })
restClient.addListInstances(mail) restClient.addListInstances(mail)
restClient.addBlobInstances(mailDetailsBlob) restClient.addBlobInstances(mailDetailsBlob)
when(mailFacade.loadMailDetailsBlob(mail)).thenResolve(mailDetails)
modelWithSpamAndInboxRule = mock( modelWithSpamAndInboxRule = mock(
new MailModel( new MailModel(
downcast({}), downcast({}),
@ -204,6 +208,7 @@ o.spec("MailModelTest", function () {
await processingDone await processingDone
verify(spamClassifier.predict(anything()), { times: 0 }) verify(spamClassifier.predict(anything()), { times: 0 })
}) })
o("spam prediction happens when inbox rule is not applied", async () => { o("spam prediction happens when inbox rule is not applied", async () => {
when(spamClassifier.predict(anything())).thenResolve(false) when(spamClassifier.predict(anything())).thenResolve(false)
@ -217,19 +222,6 @@ o.spec("MailModelTest", function () {
await processingDone await processingDone
verify(spamClassifier.predict(anything()), { times: 1 }) verify(spamClassifier.predict(anything()), { times: 1 })
}) })
o("spam prediction happens when inbox rule throws", async () => {
when(spamClassifier.predict(anything())).thenResolve(false)
const mailCreateEvent = makeUpdate({
instanceListId: "mailListId",
instanceId: "mailId",
operation: OperationType.CREATE,
})
when(inboxRuleHandler.findAndApplyMatchingRule(anything(), anything(), anything())).thenReject(new Error("Some error for inbox rule"))
const { processingDone } = await modelWithSpamAndInboxRule.entityEventsReceived([mailCreateEvent])
await processingDone
verify(spamClassifier.predict(anything()), { times: 1 })
})
o("does not try to do spam classification when downloading of mail fails on create mail event", async function () { o("does not try to do spam classification when downloading of mail fails on create mail event", async function () {
when(inboxRuleHandler.findAndApplyMatchingRule(anything(), anything(), anything())).thenResolve(null) when(inboxRuleHandler.findAndApplyMatchingRule(anything(), anything(), anything())).thenResolve(null)
@ -252,25 +244,6 @@ o.spec("MailModelTest", function () {
verify(spamClassifier.predict(anything()), { times: 1 }) verify(spamClassifier.predict(anything()), { times: 1 })
}) })
o("no spam prediction for draft mails", async () => {
mail.mailDetails = null
mail.mailDetailsDraft = ["draftListId", "draftId"]
restClient.addListInstances(mail)
when(inboxRuleHandler.findAndApplyMatchingRule(anything(), mail, anything())).thenResolve(inboxFolder)
const mailCreateEvent = makeUpdate({
instanceListId: "mailListId",
instanceId: "mailId",
operation: OperationType.CREATE,
})
const { processingDone } = await modelWithSpamAndInboxRule.entityEventsReceived([mailCreateEvent])
await processingDone
verify(spamClassificationHandler.predictSpamForNewMail(mail, anything(), anything()), { times: 1 })
verify(spamClassifier.predict(anything()), { times: 0 })
})
o("deletes a training datum for deleted mail event", async () => { o("deletes a training datum for deleted mail event", async () => {
const mailDeleteEvent = makeUpdate({ const mailDeleteEvent = makeUpdate({
instanceListId: "mailListId", instanceListId: "mailListId",

View file

@ -7,25 +7,19 @@ import {
Mail, Mail,
MailDetails, MailDetails,
MailDetailsTypeRef, MailDetailsTypeRef,
MailFolder,
MailFolderTypeRef, MailFolderTypeRef,
MailTypeRef, MailTypeRef,
} from "../../../src/common/api/entities/tutanota/TypeRefs" } from "../../../src/common/api/entities/tutanota/TypeRefs"
import { SpamClassifier, SpamTrainMailDatum } from "../../../src/mail-app/workerUtils/spamClassification/SpamClassifier" import { SpamClassifier, SpamTrainMailDatum } from "../../../src/mail-app/workerUtils/spamClassification/SpamClassifier"
import { getMailBodyText } from "../../../src/common/api/common/CommonMailUtils" import { getMailBodyText } from "../../../src/common/api/common/CommonMailUtils"
import { MailSetKind } from "../../../src/common/api/common/TutanotaConstants" import { MailSetKind, ProcessingState } from "../../../src/common/api/common/TutanotaConstants"
import { ClientClassifierType } from "../../../src/common/api/common/ClientClassifierType" import { ClientClassifierType } from "../../../src/common/api/common/ClientClassifierType"
import { assert, assertNotNull, defer, Nullable } from "@tutao/tutanota-utils" import { assert, assertNotNull } from "@tutao/tutanota-utils"
import { MailFacade } from "../../../src/common/api/worker/facades/lazy/MailFacade" import { MailFacade } from "../../../src/common/api/worker/facades/lazy/MailFacade"
import { createTestEntity } from "../TestUtils" import { createTestEntity } from "../TestUtils"
import { SpamClassificationHandler } from "../../../src/mail-app/mail/model/SpamClassificationHandler" import { SpamClassificationHandler } from "../../../src/mail-app/mail/model/SpamClassificationHandler"
import { EntityClient } from "../../../src/common/api/common/EntityClient"
import { ClientModelInfo } from "../../../src/common/api/common/EntityFunctions"
import { BulkMailLoader } from "../../../src/mail-app/workerUtils/index/BulkMailLoader"
import { EntityRestInterface } from "../../../src/common/api/worker/rest/EntityRestClient"
import { FolderSystem } from "../../../src/common/api/common/mail/FolderSystem" import { FolderSystem } from "../../../src/common/api/common/mail/FolderSystem"
import { isSameId } from "../../../src/common/api/common/utils/EntityUtils" import { isSameId } from "../../../src/common/api/common/utils/EntityUtils"
import { WebsocketConnectivityModel } from "../../../src/common/misc/WebsocketConnectivityModel"
const { anything } = matchers const { anything } = matchers
@ -35,12 +29,8 @@ o.spec("SpamClassificationHandlerTest", function () {
let mail: Mail let mail: Mail
let spamClassifier: SpamClassifier let spamClassifier: SpamClassifier
let spamHandler: SpamClassificationHandler let spamHandler: SpamClassificationHandler
let restClient: EntityRestInterface
let bulkMailLoader: BulkMailLoader
const inboxRuleOutcome = defer<Nullable<MailFolder>>()
let folderSystem: FolderSystem let folderSystem: FolderSystem
let mailDetails: MailDetails let mailDetails: MailDetails
let connectivityModel: WebsocketConnectivityModel
const inboxFolder = createTestEntity(MailFolderTypeRef, { _id: ["listId", "inbox"], folderType: MailSetKind.INBOX }) const inboxFolder = createTestEntity(MailFolderTypeRef, { _id: ["listId", "inbox"], folderType: MailSetKind.INBOX })
const trashFolder = createTestEntity(MailFolderTypeRef, { _id: ["listId", "trash"], folderType: MailSetKind.TRASH }) const trashFolder = createTestEntity(MailFolderTypeRef, { _id: ["listId", "trash"], folderType: MailSetKind.TRASH })
@ -48,7 +38,6 @@ o.spec("SpamClassificationHandlerTest", function () {
o.beforeEach(function () { o.beforeEach(function () {
spamClassifier = object<SpamClassifier>() spamClassifier = object<SpamClassifier>()
restClient = object<EntityRestInterface>()
body = createTestEntity(BodyTypeRef, { text: "Body Text" }) body = createTestEntity(BodyTypeRef, { text: "Body Text" })
mailDetails = createTestEntity(MailDetailsTypeRef, { _id: "mailDetail", body }) mailDetails = createTestEntity(MailDetailsTypeRef, { _id: "mailDetail", body })
@ -59,15 +48,11 @@ o.spec("SpamClassificationHandlerTest", function () {
_ownerGroup: "owner", _ownerGroup: "owner",
mailDetails: ["detailsList", mailDetails._id], mailDetails: ["detailsList", mailDetails._id],
unread: true, unread: true,
isInboxRuleApplied: false, processingState: ProcessingState.INBOX_RULE_NOT_PROCESSED,
clientSpamClassifierResult: null, clientSpamClassifierResult: null,
}) })
bulkMailLoader = object<BulkMailLoader>()
folderSystem = object<FolderSystem>() folderSystem = object<FolderSystem>()
connectivityModel = object<WebsocketConnectivityModel>()
when(connectivityModel.isLeader()).thenReturn(true)
when(mailFacade.simpleMoveMails(anything(), anything(), ClientClassifierType.CLIENT_CLASSIFICATION)).thenResolve([]) when(mailFacade.simpleMoveMails(anything(), anything(), ClientClassifierType.CLIENT_CLASSIFICATION)).thenResolve([])
when(folderSystem.getSystemFolderByType(MailSetKind.SPAM)).thenReturn(spamFolder) when(folderSystem.getSystemFolderByType(MailSetKind.SPAM)).thenReturn(spamFolder)
when(folderSystem.getSystemFolderByType(MailSetKind.INBOX)).thenReturn(inboxFolder) when(folderSystem.getSystemFolderByType(MailSetKind.INBOX)).thenReturn(inboxFolder)
@ -81,7 +66,7 @@ o.spec("SpamClassificationHandlerTest", function () {
else throw new Error("Unknown mail Folder") else throw new Error("Unknown mail Folder")
}) })
when( when(
bulkMailLoader.loadMailDetails( mailFacade.loadMailDetailsBlob(
matchers.argThat((requestedMails: Array<Mail>) => { matchers.argThat((requestedMails: Array<Mail>) => {
assert(requestedMails.length === 1, "exactly one mail is requested at a time") assert(requestedMails.length === 1, "exactly one mail is requested at a time")
return isSameId(requestedMails[0]._id, mail._id) return isSameId(requestedMails[0]._id, mail._id)
@ -89,14 +74,10 @@ o.spec("SpamClassificationHandlerTest", function () {
), ),
anything(), anything(),
).thenDo(async () => [{ mail, mailDetails }]) ).thenDo(async () => [{ mail, mailDetails }])
const entityClient = new EntityClient(restClient, ClientModelInfo.getNewInstanceForTestsOnly())
spamHandler = new SpamClassificationHandler(mailFacade, spamClassifier) spamHandler = new SpamClassificationHandler(mailFacade, spamClassifier)
}) })
o("processSpam correctly verifies if email is stored in spam folder", async function () { o("processSpam correctly verifies if email is stored in spam folder", async function () {
inboxRuleOutcome.resolve(null)
mail.sets = [spamFolder._id] mail.sets = [spamFolder._id]
when(spamClassifier.predict(anything())).thenResolve(false) when(spamClassifier.predict(anything())).thenResolve(false)
const expectedTrainingData: SpamTrainMailDatum = { const expectedTrainingData: SpamTrainMailDatum = {
@ -108,7 +89,7 @@ o.spec("SpamClassificationHandlerTest", function () {
isSpamConfidence: 1, isSpamConfidence: 1,
} }
const finalResult = await spamHandler.predictSpamForNewMail(mail, folderSystem) const finalResult = await spamHandler.predictSpamForNewMail(mail, mailDetails, spamFolder, folderSystem)
verify(spamClassifier.storeSpamClassification(expectedTrainingData), { times: 1 }) verify(spamClassifier.storeSpamClassification(expectedTrainingData), { times: 1 })
verify(mailFacade.simpleMoveMails([mail._id], MailSetKind.INBOX, ClientClassifierType.CLIENT_CLASSIFICATION)) verify(mailFacade.simpleMoveMails([mail._id], MailSetKind.INBOX, ClientClassifierType.CLIENT_CLASSIFICATION))
o(finalResult).deepEquals(inboxFolder) o(finalResult).deepEquals(inboxFolder)
@ -116,9 +97,8 @@ o.spec("SpamClassificationHandlerTest", function () {
o("does not classify mail if the mail has non null client classification result", async function () { o("does not classify mail if the mail has non null client classification result", async function () {
mail.sets = [inboxFolder._id] mail.sets = [inboxFolder._id]
mail.isInboxRuleApplied = false mail.processingState = ProcessingState.INBOX_RULE_NOT_PROCESSED
mail.clientSpamClassifierResult = createTestEntity(ClientSpamClassifierResultTypeRef) mail.clientSpamClassifierResult = createTestEntity(ClientSpamClassifierResultTypeRef)
inboxRuleOutcome.resolve(null)
const expectedTrainingData: SpamTrainMailDatum = { const expectedTrainingData: SpamTrainMailDatum = {
mailId: mail._id, mailId: mail._id,
@ -129,34 +109,14 @@ o.spec("SpamClassificationHandlerTest", function () {
isSpamConfidence: 0, isSpamConfidence: 0,
} }
const finalResult = await spamHandler.predictSpamForNewMail(mail, folderSystem) const finalResult = await spamHandler.predictSpamForNewMail(mail, mailDetails, inboxFolder, folderSystem)
o(finalResult).equals(null) o(finalResult).equals(null)
verify(mailFacade.simpleMoveMails(anything(), anything(), anything()), { times: 0 }) verify(mailFacade.simpleMoveMails(anything(), anything(), anything()), { times: 0 })
verify(spamClassifier.predict(anything()), { times: 0 }) verify(spamClassifier.predict(anything()), { times: 0 })
verify(spamClassifier.storeSpamClassification(expectedTrainingData), { times: 1 }) verify(spamClassifier.storeSpamClassification(expectedTrainingData), { times: 1 })
}) })
o("mail in spam folder is not classified but stored with confidence 0", async function () {
inboxRuleOutcome.resolve(null)
mail.sets = [trashFolder._id]
const expectedTrainingData: SpamTrainMailDatum = {
mailId: mail._id,
subject: mail.subject,
body: getMailBodyText(body),
isSpam: false,
ownerGroup: "owner",
isSpamConfidence: 1,
}
const finalResult = await spamHandler.predictSpamForNewMail(inboxRuleOutcome.promise, mail, folderSystem)
o(finalResult).deepEquals(null)
verify(mailFacade.simpleMoveMails(anything(), anything(), anything()), { times: 0 })
verify(spamClassifier.storeSpamClassification(expectedTrainingData), { times: 1 })
})
o("processSpam moves mail to inbox when detected as such and its not already in inbox", async function () { o("processSpam moves mail to inbox when detected as such and its not already in inbox", async function () {
inboxRuleOutcome.resolve(null)
when(spamClassifier.predict(anything())).thenResolve(false) when(spamClassifier.predict(anything())).thenResolve(false)
mail.sets = [spamFolder._id] mail.sets = [spamFolder._id]
@ -170,14 +130,13 @@ o.spec("SpamClassificationHandlerTest", function () {
ownerGroup: "owner", ownerGroup: "owner",
} }
const finalResult = await spamHandler.predictSpamForNewMail(inboxRuleOutcome.promise, mail, folderSystem) const finalResult = await spamHandler.predictSpamForNewMail(mail, mailDetails, spamFolder, folderSystem)
o(finalResult).deepEquals(inboxFolder) o(finalResult).deepEquals(inboxFolder)
verify(spamClassifier.storeSpamClassification(expectedDatum), { times: 1 }) verify(spamClassifier.storeSpamClassification(expectedDatum), { times: 1 })
verify(mailFacade.simpleMoveMails([["listId", "elementId"]], MailSetKind.INBOX, ClientClassifierType.CLIENT_CLASSIFICATION), { times: 1 }) verify(mailFacade.simpleMoveMails([["listId", "elementId"]], MailSetKind.INBOX, ClientClassifierType.CLIENT_CLASSIFICATION), { times: 1 })
}) })
o("processSpam moves mail to spam when detected as such and its not already in spam", async function () { o("processSpam moves mail to spam when detected as such and its not already in spam", async function () {
inboxRuleOutcome.resolve(null)
when(spamClassifier.predict(anything())).thenResolve(true) when(spamClassifier.predict(anything())).thenResolve(true)
mail.sets = [inboxFolder._id] mail.sets = [inboxFolder._id]
@ -190,7 +149,7 @@ o.spec("SpamClassificationHandlerTest", function () {
ownerGroup: "owner", ownerGroup: "owner",
} }
const finalResult = await spamHandler.predictSpamForNewMail(inboxRuleOutcome.promise, mail, folderSystem) const finalResult = await spamHandler.predictSpamForNewMail(mail, mailDetails, inboxFolder, folderSystem)
o(finalResult).deepEquals(spamFolder) o(finalResult).deepEquals(spamFolder)
verify(spamClassifier.storeSpamClassification(expectedDatum), { times: 1 }) verify(spamClassifier.storeSpamClassification(expectedDatum), { times: 1 })
verify(mailFacade.simpleMoveMails([["listId", "elementId"]], MailSetKind.SPAM, ClientClassifierType.CLIENT_CLASSIFICATION), { times: 1 }) verify(mailFacade.simpleMoveMails([["listId", "elementId"]], MailSetKind.SPAM, ClientClassifierType.CLIENT_CLASSIFICATION), { times: 1 })