mirror of
https://github.com/tutao/tutanota.git
synced 2025-10-19 16:03:43 +00:00
wip: tests2
This commit is contained in:
parent
1bc8ebdc4e
commit
3f09b0f5db
5 changed files with 53 additions and 11 deletions
|
@ -242,6 +242,12 @@ export class MailModel {
|
|||
mailFolderAfterInboxRuleAndSpamProcessing.then((targetFolder) => {
|
||||
this._showNotification(targetFolder ?? initialMailFolder, mail)
|
||||
})
|
||||
} else if (isUpdateForTypeRef(MailTypeRef, update) && update.operation === OperationType.DELETE) {
|
||||
const mailId: IdTuple = [update.instanceListId, update.instanceListId]
|
||||
|
||||
// todo: how can we get the group in case of delete events?
|
||||
const mailOwnerGroup = this.logins.getUserController().getMailGroupMemberships().at(0)!.group
|
||||
await this.spamHandler().dropClassificationData(mailOwnerGroup, mailId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,10 @@ export class SpamClassificationHandler {
|
|||
return assertNotNull(folderSystem.getSystemFolderByType(classifierMailSetTarget), `Could not get System folder for owner: ${mail._ownerGroup}`)
|
||||
}
|
||||
|
||||
public async dropClassificationData(mailOwnerGroup: Id, mailId: IdTuple) {
|
||||
await this.spamClassifier?.deleteSpamClassification(mailOwnerGroup, mailId)
|
||||
}
|
||||
|
||||
public async updateSpamClassificationData(events: ReadonlyArray<EntityUpdateData>, mail: Mail, folderSystem: FolderSystem) {
|
||||
// TODO:
|
||||
// would be nice to still update spam classification data even if spam classifier is not there yet,
|
||||
|
|
|
@ -200,6 +200,18 @@ export class OfflineStoragePersistence {
|
|||
await this.sqlCipherFacade.run(query, params)
|
||||
}
|
||||
|
||||
async deleteSpamClassificationData(owner: Id, mailId: IdTuple): Promise<void> {
|
||||
const mailListId = listIdPart(mailId)
|
||||
const mailElementId = elementIdPart(mailId)
|
||||
const { query, params } = sql`
|
||||
DELETE
|
||||
FROM spam_classification_training_data
|
||||
where ownerGroup = ${owner}
|
||||
AND listId = ${mailListId}
|
||||
AND elementId = ${mailElementId}`
|
||||
await this.sqlCipherFacade.run(query, params)
|
||||
}
|
||||
|
||||
async updateSpamClassificationData(id: IdTuple, isSpam: boolean, isSpamConfidence: number): Promise<void> {
|
||||
const { query, params } = sql`
|
||||
UPDATE spam_classification_training_data
|
||||
|
|
|
@ -399,6 +399,10 @@ export class SpamClassifier {
|
|||
return this.offlineStorage.storeSpamClassification(spamTrainMailDatum)
|
||||
}
|
||||
|
||||
public deleteSpamClassification(ownerGroup: Id, mailId: IdTuple) {
|
||||
return this.offlineStorage.deleteSpamClassificationData(ownerGroup, mailId)
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Only for internal release
|
||||
*
|
||||
|
@ -598,17 +602,17 @@ export class SpamClassifier {
|
|||
return concatenated.length > 0 ? concatenated : " "
|
||||
}
|
||||
|
||||
private async retrainModelFromScratch(storage: CacheStorage, ownerGroup: Id, cutoffTimestamp: number) {
|
||||
console.log("Model is being re-trained from scratch, deleting old data")
|
||||
try {
|
||||
await assertNotNull(this.offlineStorage).deleteSpamClassificationTrainingDataBeforeCutoff(cutoffTimestamp, ownerGroup)
|
||||
} catch (e) {
|
||||
console.error("Failed delete old training data: ", e)
|
||||
return
|
||||
}
|
||||
private async retrainModelFromScratch(storage: CacheStorage, ownerGroup: Id, cutoffTimestamp: number) {
|
||||
console.log("Model is being re-trained from scratch, deleting old data")
|
||||
try {
|
||||
await assertNotNull(this.offlineStorage).deleteSpamClassificationTrainingDataBeforeCutoff(cutoffTimestamp, ownerGroup)
|
||||
} catch (e) {
|
||||
console.error("Failed delete old training data: ", e)
|
||||
return
|
||||
}
|
||||
|
||||
await this.trainFromScratch(storage, ownerGroup)
|
||||
}
|
||||
await this.trainFromScratch(storage, ownerGroup)
|
||||
}
|
||||
|
||||
// === Testing methods
|
||||
public async cloneClassifier(): Promise<SpamClassifier> {
|
||||
|
|
|
@ -190,7 +190,7 @@ o.spec("MailModelTest", function () {
|
|||
const inboxRuleTargetFolder = createTestEntity(MailFolderTypeRef, { _id: ["folderListId", "inboxRuleTarget"] })
|
||||
|
||||
when(spamClassificationHandler.downloadMail(anything())).thenResolve(mail)
|
||||
when(inboxRuleHandler.findAndApplyMatchingRule(anything(), anything(), anything())).thenResolve(inboxRuleTargetFolder)
|
||||
when(inboxRuleHandler.findAndApplyMatchingRule(anything(), mail, anything())).thenResolve(inboxRuleTargetFolder)
|
||||
|
||||
const mailCreateEvent = makeUpdate({ instanceListId: "mailListId", instanceId: "mailId", operation: OperationType.CREATE })
|
||||
await model.entityEventsReceived([mailCreateEvent])
|
||||
|
@ -198,6 +198,22 @@ o.spec("MailModelTest", function () {
|
|||
verify(spamClassificationHandler.predictSpamForNewMail(anything(), mail, anything()), { times: 1 })
|
||||
verify(spamClassifier.predict(anything()), { times: 0 })
|
||||
})
|
||||
|
||||
o("deletes a training datum for deleted mail event", async () => {
|
||||
const mail = createTestEntity(MailTypeRef, {
|
||||
_id: ["mailListId", "mailId"],
|
||||
_ownerGroup: "owner",
|
||||
mailDetailsDraft: ["draftListId", "draftId"],
|
||||
mailDetails: null,
|
||||
})
|
||||
when(spamClassificationHandler.downloadMail(anything())).thenResolve(mail)
|
||||
when(inboxRuleHandler.findAndApplyMatchingRule(anything(), anything(), anything())).thenResolve(null)
|
||||
|
||||
const mailDeleteEvent = makeUpdate({ instanceListId: "mailListId", instanceId: "mailId", operation: OperationType.DELETE })
|
||||
await model.entityEventsReceived([mailDeleteEvent])
|
||||
|
||||
verify(spamClassifier.deleteSpamClassification("owner", mail._id), { times: 0 })
|
||||
})
|
||||
})
|
||||
|
||||
function makeUpdate({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue