mirror of
https://github.com/zadam/trilium.git
synced 2026-05-07 11:06:20 +02:00
chore(core): introduce becca_easy_mocking and becca_mocking
This commit is contained in:
@@ -1,134 +1,3 @@
|
||||
import { NoteType } from "@triliumnext/commons";
|
||||
|
||||
import BAttachment from "../becca/entities/battachment.js";
|
||||
import BAttribute from "../becca/entities/battribute.js";
|
||||
import BBranch from "../becca/entities/bbranch.js";
|
||||
import BNote from "../becca/entities/bnote.js";
|
||||
import utils, { randomString } from "../services/utils.js";
|
||||
|
||||
type AttributeDefinitions = { [key in `#${string}`]: string; };
|
||||
type RelationDefinitions = { [key in `~${string}`]: string; };
|
||||
|
||||
interface NoteDefinition extends AttributeDefinitions, RelationDefinitions {
|
||||
id?: string | undefined;
|
||||
title?: string;
|
||||
content?: string;
|
||||
type?: NoteType;
|
||||
mime?: string;
|
||||
children?: NoteDefinition[];
|
||||
attachments?: {
|
||||
title: string;
|
||||
role: string;
|
||||
mime: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the given notes with the given title and optionally one or more attributes.
|
||||
*
|
||||
* For a label to be created, simply pass on a key prefixed with `#` and any desired value.
|
||||
*
|
||||
* The notes and attributes will be injected in the froca.
|
||||
*
|
||||
* @param notes
|
||||
* @returns an array containing the IDs of the created notes.
|
||||
* @example
|
||||
* buildNotes([
|
||||
* { title: "A", "#startDate": "2025-05-05" },
|
||||
* { title: "B", "#startDate": "2025-05-07" }
|
||||
* ]);
|
||||
*/
|
||||
export function buildNotes(notes: NoteDefinition[]) {
|
||||
const ids: string[] = [];
|
||||
|
||||
for (const noteDef of notes) {
|
||||
ids.push(buildNote(noteDef).noteId);
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
export function buildNote(noteDef: NoteDefinition) {
|
||||
const note = new BNote({
|
||||
noteId: noteDef.id ?? utils.randomString(12),
|
||||
title: noteDef.title ?? "New note",
|
||||
type: noteDef.type ?? "text",
|
||||
mime: noteDef.mime ?? "text/html",
|
||||
isProtected: false,
|
||||
blobId: ""
|
||||
});
|
||||
|
||||
// Handle content.
|
||||
if (noteDef.content !== undefined) {
|
||||
note.getContent = () => noteDef.content!;
|
||||
}
|
||||
|
||||
// Handle children
|
||||
if (noteDef.children) {
|
||||
for (const childDef of noteDef.children) {
|
||||
const childNote = buildNote(childDef);
|
||||
new BBranch({
|
||||
noteId: childNote.noteId,
|
||||
parentNoteId: note.noteId,
|
||||
branchId: `${note.noteId}_${childNote.noteId}`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Handle labels and relations.
|
||||
let position = 0;
|
||||
for (const [ key, value ] of Object.entries(noteDef)) {
|
||||
const attributeId = utils.randomString(12);
|
||||
const name = key.substring(1);
|
||||
|
||||
let attribute: BAttribute | null = null;
|
||||
if (key.startsWith("#")) {
|
||||
attribute = new BAttribute({
|
||||
noteId: note.noteId,
|
||||
attributeId,
|
||||
type: "label",
|
||||
name,
|
||||
value,
|
||||
position,
|
||||
isInheritable: false
|
||||
});
|
||||
}
|
||||
|
||||
if (key.startsWith("~")) {
|
||||
attribute = new BAttribute({
|
||||
noteId: note.noteId,
|
||||
attributeId,
|
||||
type: "relation",
|
||||
name,
|
||||
value,
|
||||
position,
|
||||
isInheritable: false
|
||||
});
|
||||
}
|
||||
|
||||
if (!attribute) {
|
||||
continue;
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
|
||||
// Handle attachments.
|
||||
if (noteDef.attachments) {
|
||||
const allAttachments: BAttachment[] = [];
|
||||
for (const { title, role, mime } of noteDef.attachments) {
|
||||
const attachment = new BAttachment({
|
||||
attachmentId: randomString(10),
|
||||
ownerId: note.noteId,
|
||||
title,
|
||||
role,
|
||||
mime
|
||||
});
|
||||
allAttachments.push(attachment);
|
||||
}
|
||||
|
||||
note.getAttachmentsByRole = (role) => allAttachments.filter(a => a.role === role);
|
||||
}
|
||||
|
||||
return note;
|
||||
}
|
||||
import { becca_easy_mocking } from "@triliumnext/core";
|
||||
export const buildNote = becca_easy_mocking.buildNote;
|
||||
export const buildNotes = becca_easy_mocking.buildNotes;
|
||||
|
||||
@@ -1,77 +1,2 @@
|
||||
import type { NoteRow, NoteType } from "@triliumnext/commons";
|
||||
import { SearchResult } from "@triliumnext/core";
|
||||
import randtoken from "rand-token";
|
||||
|
||||
import becca from "../becca/becca.js";
|
||||
import BAttribute from "../becca/entities/battribute.js";
|
||||
import BBranch from "../becca/entities/bbranch.js";
|
||||
import BNote from "../becca/entities/bnote.js";
|
||||
randtoken.generator({ source: "crypto" });
|
||||
|
||||
export function findNoteByTitle(searchResults: Array<SearchResult>, title: string): BNote | undefined {
|
||||
return searchResults.map((sr) => becca.notes[sr.noteId]).find((note) => note.title === title);
|
||||
}
|
||||
|
||||
export class NoteBuilder {
|
||||
note: BNote;
|
||||
constructor(note: BNote) {
|
||||
this.note = note;
|
||||
}
|
||||
|
||||
label(name: string, value = "", isInheritable = false) {
|
||||
new BAttribute({
|
||||
attributeId: id(),
|
||||
noteId: this.note.noteId,
|
||||
type: "label",
|
||||
isInheritable,
|
||||
name,
|
||||
value
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
relation(name: string, targetNote: BNote) {
|
||||
new BAttribute({
|
||||
attributeId: id(),
|
||||
noteId: this.note.noteId,
|
||||
type: "relation",
|
||||
name,
|
||||
value: targetNote.noteId
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
child(childNoteBuilder: NoteBuilder, prefix = "") {
|
||||
new BBranch({
|
||||
branchId: id(),
|
||||
noteId: childNoteBuilder.note.noteId,
|
||||
parentNoteId: this.note.noteId,
|
||||
prefix,
|
||||
notePosition: 10
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export function id() {
|
||||
return randtoken.generate(10);
|
||||
}
|
||||
|
||||
export function note(title: string, extraParams: Partial<NoteRow> = {}) {
|
||||
const row = Object.assign(
|
||||
{
|
||||
noteId: id(),
|
||||
title,
|
||||
type: "text" as NoteType,
|
||||
mime: "text/html"
|
||||
},
|
||||
extraParams
|
||||
);
|
||||
|
||||
const note = new BNote(row);
|
||||
|
||||
return new NoteBuilder(note);
|
||||
}
|
||||
import { becca_mocking } from "@triliumnext/core";
|
||||
export const NoteBuilder = becca_mocking.NoteBuilder;
|
||||
|
||||
@@ -98,6 +98,9 @@ export { t } from "i18next";
|
||||
export type { RequestProvider, ExecOpts, CookieJar } from "./services/request";
|
||||
export type * from "./meta";
|
||||
|
||||
export * as becca_easy_mocking from "./test/becca_easy_mocking";
|
||||
export * as becca_mocking from "./test/becca_mocking";
|
||||
|
||||
export async function initializeCore({ dbConfig, executionContext, crypto, translations, messaging, request, schema, extraAppInfo, platform }: {
|
||||
dbConfig: SqlServiceParams,
|
||||
executionContext: ExecutionContext,
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
export default {
|
||||
saveImageToAttachment(noteId: string, imageBuffer: Uint8Array, title: string, b1: boolean, b2: boolean) {
|
||||
console.warn("Image save ignored", noteId, title);
|
||||
|
||||
return {
|
||||
attachmentId: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -710,22 +710,22 @@ describe("Search", () => {
|
||||
// Create a moderate-sized dataset to test performance
|
||||
const countries = ["Austria", "Belgium", "Croatia", "Denmark", "Estonia", "Finland", "Germany", "Hungary", "Ireland", "Japan"];
|
||||
const europeanCountries = note("Europe");
|
||||
|
||||
|
||||
countries.forEach(country => {
|
||||
europeanCountries.child(note(country).label("type", "country").label("continent", "Europe"));
|
||||
});
|
||||
|
||||
|
||||
rootNote.child(europeanCountries);
|
||||
|
||||
const searchContext = new SearchContext();
|
||||
const startTime = Date.now();
|
||||
|
||||
|
||||
// Perform a search that exercises multiple features
|
||||
const searchResults = searchService.findResultsWithQuery("#type=country AND continent", searchContext);
|
||||
|
||||
|
||||
const endTime = Date.now();
|
||||
const duration = endTime - startTime;
|
||||
|
||||
|
||||
// Search should complete in under 1 second for reasonable dataset
|
||||
expect(duration).toBeLessThan(1000);
|
||||
expect(searchResults.length).toEqual(10);
|
||||
@@ -748,14 +748,14 @@ describe("Search", () => {
|
||||
|
||||
// Get note titles in result order
|
||||
const resultTitles = searchResults.map(r => becca.notes[r.noteId].title);
|
||||
|
||||
|
||||
// Find all exact matches (contain "analysis")
|
||||
const exactMatchIndices = resultTitles.map((title, index) =>
|
||||
const exactMatchIndices = resultTitles.map((title, index) =>
|
||||
title.toLowerCase().includes("analysis") ? index : -1
|
||||
).filter(index => index !== -1);
|
||||
|
||||
|
||||
// Find all fuzzy matches (contain typos)
|
||||
const fuzzyMatchIndices = resultTitles.map((title, index) =>
|
||||
const fuzzyMatchIndices = resultTitles.map((title, index) =>
|
||||
(title.includes("Anaylsis") || title.includes("Anlaysis")) ? index : -1
|
||||
).filter(index => index !== -1);
|
||||
|
||||
@@ -765,7 +765,7 @@ describe("Search", () => {
|
||||
// CRITICAL: All exact matches must appear before all fuzzy matches
|
||||
const lastExactIndex = Math.max(...exactMatchIndices);
|
||||
const firstFuzzyIndex = Math.min(...fuzzyMatchIndices);
|
||||
|
||||
|
||||
expect(lastExactIndex).toBeLessThan(firstFuzzyIndex);
|
||||
});
|
||||
|
||||
|
||||
134
packages/trilium-core/src/test/becca_easy_mocking.ts
Normal file
134
packages/trilium-core/src/test/becca_easy_mocking.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { NoteType } from "@triliumnext/commons";
|
||||
|
||||
import BAttachment from "../becca/entities/battachment.js";
|
||||
import BAttribute from "../becca/entities/battribute.js";
|
||||
import BBranch from "../becca/entities/bbranch.js";
|
||||
import BNote from "../becca/entities/bnote.js";
|
||||
import { randomString } from "../services/utils/index.js";
|
||||
|
||||
type AttributeDefinitions = { [key in `#${string}`]: string; };
|
||||
type RelationDefinitions = { [key in `~${string}`]: string; };
|
||||
|
||||
interface NoteDefinition extends AttributeDefinitions, RelationDefinitions {
|
||||
id?: string | undefined;
|
||||
title?: string;
|
||||
content?: string;
|
||||
type?: NoteType;
|
||||
mime?: string;
|
||||
children?: NoteDefinition[];
|
||||
attachments?: {
|
||||
title: string;
|
||||
role: string;
|
||||
mime: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the given notes with the given title and optionally one or more attributes.
|
||||
*
|
||||
* For a label to be created, simply pass on a key prefixed with `#` and any desired value.
|
||||
*
|
||||
* The notes and attributes will be injected in the froca.
|
||||
*
|
||||
* @param notes
|
||||
* @returns an array containing the IDs of the created notes.
|
||||
* @example
|
||||
* buildNotes([
|
||||
* { title: "A", "#startDate": "2025-05-05" },
|
||||
* { title: "B", "#startDate": "2025-05-07" }
|
||||
* ]);
|
||||
*/
|
||||
export function buildNotes(notes: NoteDefinition[]) {
|
||||
const ids: string[] = [];
|
||||
|
||||
for (const noteDef of notes) {
|
||||
ids.push(buildNote(noteDef).noteId);
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
export function buildNote(noteDef: NoteDefinition) {
|
||||
const note = new BNote({
|
||||
noteId: noteDef.id ?? randomString(12),
|
||||
title: noteDef.title ?? "New note",
|
||||
type: noteDef.type ?? "text",
|
||||
mime: noteDef.mime ?? "text/html",
|
||||
isProtected: false,
|
||||
blobId: ""
|
||||
});
|
||||
|
||||
// Handle content.
|
||||
if (noteDef.content !== undefined) {
|
||||
note.getContent = () => noteDef.content!;
|
||||
}
|
||||
|
||||
// Handle children
|
||||
if (noteDef.children) {
|
||||
for (const childDef of noteDef.children) {
|
||||
const childNote = buildNote(childDef);
|
||||
new BBranch({
|
||||
noteId: childNote.noteId,
|
||||
parentNoteId: note.noteId,
|
||||
branchId: `${note.noteId}_${childNote.noteId}`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Handle labels and relations.
|
||||
let position = 0;
|
||||
for (const [ key, value ] of Object.entries(noteDef)) {
|
||||
const attributeId = randomString(12);
|
||||
const name = key.substring(1);
|
||||
|
||||
let attribute: BAttribute | null = null;
|
||||
if (key.startsWith("#")) {
|
||||
attribute = new BAttribute({
|
||||
noteId: note.noteId,
|
||||
attributeId,
|
||||
type: "label",
|
||||
name,
|
||||
value,
|
||||
position,
|
||||
isInheritable: false
|
||||
});
|
||||
}
|
||||
|
||||
if (key.startsWith("~")) {
|
||||
attribute = new BAttribute({
|
||||
noteId: note.noteId,
|
||||
attributeId,
|
||||
type: "relation",
|
||||
name,
|
||||
value,
|
||||
position,
|
||||
isInheritable: false
|
||||
});
|
||||
}
|
||||
|
||||
if (!attribute) {
|
||||
continue;
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
|
||||
// Handle attachments.
|
||||
if (noteDef.attachments) {
|
||||
const allAttachments: BAttachment[] = [];
|
||||
for (const { title, role, mime } of noteDef.attachments) {
|
||||
const attachment = new BAttachment({
|
||||
attachmentId: randomString(10),
|
||||
ownerId: note.noteId,
|
||||
title,
|
||||
role,
|
||||
mime
|
||||
});
|
||||
allAttachments.push(attachment);
|
||||
}
|
||||
|
||||
note.getAttachmentsByRole = (role) => allAttachments.filter(a => a.role === role);
|
||||
}
|
||||
|
||||
return note;
|
||||
}
|
||||
76
packages/trilium-core/src/test/becca_mocking.ts
Normal file
76
packages/trilium-core/src/test/becca_mocking.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import type { NoteRow, NoteType } from "@triliumnext/commons";
|
||||
|
||||
import becca from "../becca/becca.js";
|
||||
import BAttribute from "../becca/entities/battribute.js";
|
||||
import BBranch from "../becca/entities/bbranch.js";
|
||||
import BNote from "../becca/entities/bnote.js";
|
||||
import { randomString } from "../services/utils/index.js";
|
||||
import SearchResult from "../services/search/search_result.js";
|
||||
|
||||
export function findNoteByTitle(searchResults: Array<SearchResult>, title: string): BNote | undefined {
|
||||
return searchResults.map((sr) => becca.notes[sr.noteId]).find((note) => note.title === title);
|
||||
}
|
||||
|
||||
export class NoteBuilder {
|
||||
note: BNote;
|
||||
constructor(note: BNote) {
|
||||
this.note = note;
|
||||
}
|
||||
|
||||
label(name: string, value = "", isInheritable = false) {
|
||||
new BAttribute({
|
||||
attributeId: id(),
|
||||
noteId: this.note.noteId,
|
||||
type: "label",
|
||||
isInheritable,
|
||||
name,
|
||||
value
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
relation(name: string, targetNote: BNote) {
|
||||
new BAttribute({
|
||||
attributeId: id(),
|
||||
noteId: this.note.noteId,
|
||||
type: "relation",
|
||||
name,
|
||||
value: targetNote.noteId
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
child(childNoteBuilder: NoteBuilder, prefix = "") {
|
||||
new BBranch({
|
||||
branchId: id(),
|
||||
noteId: childNoteBuilder.note.noteId,
|
||||
parentNoteId: this.note.noteId,
|
||||
prefix,
|
||||
notePosition: 10
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export function id() {
|
||||
return randomString(10);
|
||||
}
|
||||
|
||||
export function note(title: string, extraParams: Partial<NoteRow> = {}) {
|
||||
const row = Object.assign(
|
||||
{
|
||||
noteId: id(),
|
||||
title,
|
||||
type: "text" as NoteType,
|
||||
mime: "text/html"
|
||||
},
|
||||
extraParams
|
||||
);
|
||||
|
||||
const note = new BNote(row);
|
||||
|
||||
return new NoteBuilder(note);
|
||||
}
|
||||
Reference in New Issue
Block a user