From f30aad11a5281e7599434486818f62ddc6177ac3 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 21 May 2024 13:04:23 -0500 Subject: [PATCH 1/4] Fix legacy quote posts --- src/storages/hydrate.ts | 5 +++-- src/tags.ts | 11 +++++++++-- src/views/mastodon/statuses.ts | 12 ++++++------ 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/storages/hydrate.ts b/src/storages/hydrate.ts index e5c488e3..f55d7414 100644 --- a/src/storages/hydrate.ts +++ b/src/storages/hydrate.ts @@ -6,6 +6,7 @@ import { type DittoEvent } from '@/interfaces/DittoEvent.ts'; import { DittoTables } from '@/db/DittoTables.ts'; import { Conf } from '@/config.ts'; import { refreshAuthorStatsDebounced } from '@/stats.ts'; +import { findQuoteTag } from '@/tags.ts'; interface HydrateOpts { events: DittoEvent[]; @@ -81,7 +82,7 @@ function assembleEvents( event.user = b.find((e) => matchFilter({ kinds: [30361], authors: [admin], '#d': [event.pubkey] }, e)); if (event.kind === 1) { - const id = event.tags.find(([name]) => name === 'q')?.[1]; + const id = findQuoteTag(event.tags)?.[1]; if (id) { event.quote = b.find((e) => matchFilter({ kinds: [1], ids: [id] }, e)); } @@ -169,7 +170,7 @@ function gatherQuotes({ events, store, signal }: HydrateOpts): Promise name === 'q')?.[1]; + const id = findQuoteTag(event.tags)?.[1]; if (id) { ids.add(id); } diff --git a/src/tags.ts b/src/tags.ts index a683393e..ecddaf4b 100644 --- a/src/tags.ts +++ b/src/tags.ts @@ -35,8 +35,15 @@ const isReplyTag = (tag: string[]) => tag[0] === 'e' && tag[3] === 'reply'; const isRootTag = (tag: string[]) => tag[0] === 'e' && tag[3] === 'root'; const isLegacyReplyTag = (tag: string[]) => tag[0] === 'e' && !tag[3]; -function findReplyTag(tags: string[][]) { +const isQuoteTag = (tag: string[]) => tag[0] === 'q'; +const isLegacyQuoteTag = (tag: string[]) => tag[0] === 'e' && tag[3] === 'mention'; + +function findReplyTag(tags: string[][]): string[] | undefined { return tags.find(isReplyTag) || tags.find(isRootTag) || tags.findLast(isLegacyReplyTag); } -export { addTag, deleteTag, findReplyTag, getTagSet, hasTag }; +function findQuoteTag(tags: string[][]): string[] | undefined { + return tags.find(isQuoteTag) || tags.find(isLegacyQuoteTag); +} + +export { addTag, deleteTag, findQuoteTag, findReplyTag, getTagSet, hasTag }; diff --git a/src/views/mastodon/statuses.ts b/src/views/mastodon/statuses.ts index a06aac21..15719778 100644 --- a/src/views/mastodon/statuses.ts +++ b/src/views/mastodon/statuses.ts @@ -1,11 +1,10 @@ import { NostrEvent } from '@nostrify/nostrify'; -import { isCWTag } from 'https://gitlab.com/soapbox-pub/mostr/-/raw/c67064aee5ade5e01597c6d23e22e53c628ef0e2/src/nostr/tags.ts'; import { nip19 } from 'nostr-tools'; import { Conf } from '@/config.ts'; import { type DittoEvent } from '@/interfaces/DittoEvent.ts'; import { Storages } from '@/storages.ts'; -import { findReplyTag } from '@/tags.ts'; +import { findQuoteTag, findReplyTag } from '@/tags.ts'; import { nostrDate } from '@/utils.ts'; import { getMediaLinks, parseNoteContent, stripimeta } from '@/utils/note.ts'; import { unfurlCardCached } from '@/utils/unfurl.ts'; @@ -30,6 +29,7 @@ async function renderStatus(event: DittoEvent, opts: RenderStatusOpts): Promise< : await accountFromPubkey(event.pubkey); const replyTag = findReplyTag(event.tags); + const quoteTag = findQuoteTag(event.tags); const mentionedPubkeys = [ ...new Set( @@ -73,8 +73,8 @@ async function renderStatus(event: DittoEvent, opts: RenderStatusOpts): Promise< const content = buildInlineRecipients(mentions) + html; - const cw = event.tags.find(isCWTag); - const subject = event.tags.find((tag) => tag[0] === 'subject'); + const cw = event.tags.find(([name]) => name === 'content-warning'); + const subject = event.tags.find(([name]) => name === 'subject'); const imeta: string[][][] = event.tags .filter(([name]) => name === 'imeta') @@ -88,7 +88,7 @@ async function renderStatus(event: DittoEvent, opts: RenderStatusOpts): Promise< card, content, created_at: nostrDate(event.created_at).toISOString(), - in_reply_to_id: replyTag ? replyTag[1] : null, + in_reply_to_id: replyTag?.[1] ?? null, in_reply_to_account_id: null, sensitive: !!cw, spoiler_text: (cw ? cw[1] : subject?.[1]) || '', @@ -110,7 +110,7 @@ async function renderStatus(event: DittoEvent, opts: RenderStatusOpts): Promise< emojis: renderEmojis(event), poll: null, quote: !event.quote ? null : await renderStatus(event.quote, { depth: depth + 1 }), - quote_id: event.tags.find(([name]) => name === 'q')?.[1] ?? null, + quote_id: quoteTag?.[1] ?? null, uri: Conf.external(note), url: Conf.external(note), zapped: Boolean(zapEvent), From 9839b8138f36baa55240a38748049f67bc1b0c08 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 21 May 2024 13:08:08 -0500 Subject: [PATCH 2/4] tags.ts -> utils/tags.ts --- src/controllers/api/accounts.ts | 2 +- src/controllers/api/admin.ts | 2 +- src/controllers/api/bookmarks.ts | 2 +- src/controllers/api/mutes.ts | 2 +- src/controllers/api/statuses.ts | 8 ++++---- src/controllers/api/suggestions.ts | 2 +- src/pipeline.ts | 2 +- src/policies/MuteListPolicy.ts | 2 +- src/queries.ts | 2 +- src/stats.ts | 2 +- src/storages/EventsDB.ts | 4 ++-- src/storages/UserStore.ts | 2 +- src/storages/hydrate.ts | 2 +- src/{ => utils}/tags.ts | 0 src/views/mastodon/relationships.ts | 2 +- src/views/mastodon/statuses.ts | 2 +- 16 files changed, 19 insertions(+), 19 deletions(-) rename src/{ => utils}/tags.ts (100%) diff --git a/src/controllers/api/accounts.ts b/src/controllers/api/accounts.ts index f717be39..2d61adcd 100644 --- a/src/controllers/api/accounts.ts +++ b/src/controllers/api/accounts.ts @@ -7,7 +7,6 @@ import { Conf } from '@/config.ts'; import { getAuthor, getFollowedPubkeys } from '@/queries.ts'; import { booleanParamSchema, fileSchema } from '@/schema.ts'; import { Storages } from '@/storages.ts'; -import { addTag, deleteTag, findReplyTag, getTagSet } from '@/tags.ts'; import { uploadFile } from '@/utils/upload.ts'; import { nostrNow } from '@/utils.ts'; import { createEvent, paginated, paginationSchema, parseBody, updateListEvent } from '@/utils/api.ts'; @@ -18,6 +17,7 @@ import { renderRelationship } from '@/views/mastodon/relationships.ts'; import { renderStatus } from '@/views/mastodon/statuses.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; import { bech32ToPubkey } from '@/utils.ts'; +import { addTag, deleteTag, findReplyTag, getTagSet } from '@/utils/tags.ts'; const usernameSchema = z .string().min(1).max(30) diff --git a/src/controllers/api/admin.ts b/src/controllers/api/admin.ts index 77571aa7..d7cd3658 100644 --- a/src/controllers/api/admin.ts +++ b/src/controllers/api/admin.ts @@ -5,8 +5,8 @@ import { Conf } from '@/config.ts'; import { DittoEvent } from '@/interfaces/DittoEvent.ts'; import { booleanParamSchema } from '@/schema.ts'; import { Storages } from '@/storages.ts'; -import { addTag } from '@/tags.ts'; import { paginated, paginationSchema, parseBody, updateListAdminEvent } from '@/utils/api.ts'; +import { addTag } from '@/utils/tags.ts'; import { renderAdminAccount } from '@/views/mastodon/admin-accounts.ts'; const adminAccountQuerySchema = z.object({ diff --git a/src/controllers/api/bookmarks.ts b/src/controllers/api/bookmarks.ts index 76551827..6d80b500 100644 --- a/src/controllers/api/bookmarks.ts +++ b/src/controllers/api/bookmarks.ts @@ -1,6 +1,6 @@ import { type AppController } from '@/app.ts'; import { Storages } from '@/storages.ts'; -import { getTagSet } from '@/tags.ts'; +import { getTagSet } from '@/utils/tags.ts'; import { renderStatuses } from '@/views.ts'; /** https://docs.joinmastodon.org/methods/bookmarks/#get */ diff --git a/src/controllers/api/mutes.ts b/src/controllers/api/mutes.ts index 4afb6c40..31f54ee1 100644 --- a/src/controllers/api/mutes.ts +++ b/src/controllers/api/mutes.ts @@ -1,6 +1,6 @@ import { type AppController } from '@/app.ts'; import { Storages } from '@/storages.ts'; -import { getTagSet } from '@/tags.ts'; +import { getTagSet } from '@/utils/tags.ts'; import { renderAccounts } from '@/views.ts'; /** https://docs.joinmastodon.org/methods/mutes/#get */ diff --git a/src/controllers/api/statuses.ts b/src/controllers/api/statuses.ts index 291d970c..e9c872f8 100644 --- a/src/controllers/api/statuses.ts +++ b/src/controllers/api/statuses.ts @@ -8,15 +8,15 @@ import { Conf } from '@/config.ts'; import { DittoDB } from '@/db/DittoDB.ts'; import { getUnattachedMediaByIds } from '@/db/unattached-media.ts'; import { getAncestors, getAuthor, getDescendants, getEvent } from '@/queries.ts'; -import { addTag, deleteTag } from '@/tags.ts'; -import { createEvent, paginationSchema, parseBody, updateListEvent } from '@/utils/api.ts'; import { renderEventAccounts } from '@/views.ts'; import { renderReblog, renderStatus } from '@/views/mastodon/statuses.ts'; -import { getLnurl } from '@/utils/lnurl.ts'; -import { asyncReplaceAll } from '@/utils/text.ts'; import { Storages } from '@/storages.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; +import { createEvent, paginationSchema, parseBody, updateListEvent } from '@/utils/api.ts'; +import { getLnurl } from '@/utils/lnurl.ts'; import { lookupPubkey } from '@/utils/lookup.ts'; +import { addTag, deleteTag } from '@/utils/tags.ts'; +import { asyncReplaceAll } from '@/utils/text.ts'; const createStatusSchema = z.object({ in_reply_to_id: z.string().regex(/[0-9a-f]{64}/).nullish(), diff --git a/src/controllers/api/suggestions.ts b/src/controllers/api/suggestions.ts index 6377bd4f..012244a1 100644 --- a/src/controllers/api/suggestions.ts +++ b/src/controllers/api/suggestions.ts @@ -2,8 +2,8 @@ import { NStore } from '@nostrify/nostrify'; import { AppController } from '@/app.ts'; import { Conf } from '@/config.ts'; -import { getTagSet } from '@/tags.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; +import { getTagSet } from '@/utils/tags.ts'; import { accountFromPubkey, renderAccount } from '@/views/mastodon/accounts.ts'; export const suggestionsV1Controller: AppController = async (c) => { diff --git a/src/pipeline.ts b/src/pipeline.ts index 15d495e7..bfb0577e 100644 --- a/src/pipeline.ts +++ b/src/pipeline.ts @@ -13,7 +13,6 @@ import { RelayError } from '@/RelayError.ts'; import { updateStats } from '@/stats.ts'; import { hydrateEvents, purifyEvent } from '@/storages/hydrate.ts'; import { Storages } from '@/storages.ts'; -import { getTagSet } from '@/tags.ts'; import { eventAge, nostrDate, nostrNow, parseNip05, Time } from '@/utils.ts'; import { fetchWorker } from '@/workers/fetch.ts'; import { policyWorker } from '@/workers/policy.ts'; @@ -22,6 +21,7 @@ import { verifyEventWorker } from '@/workers/verify.ts'; import { AdminSigner } from '@/signers/AdminSigner.ts'; import { lnurlCache } from '@/utils/lnurl.ts'; import { nip05Cache } from '@/utils/nip05.ts'; +import { getTagSet } from '@/utils/tags.ts'; import { MuteListPolicy } from '@/policies/MuteListPolicy.ts'; diff --git a/src/policies/MuteListPolicy.ts b/src/policies/MuteListPolicy.ts index cae08eba..130d10df 100644 --- a/src/policies/MuteListPolicy.ts +++ b/src/policies/MuteListPolicy.ts @@ -1,6 +1,6 @@ import { NostrEvent, NostrRelayOK, NPolicy, NStore } from '@nostrify/nostrify'; -import { getTagSet } from '@/tags.ts'; +import { getTagSet } from '@/utils/tags.ts'; export class MuteListPolicy implements NPolicy { constructor(private pubkey: string, private store: NStore) {} diff --git a/src/queries.ts b/src/queries.ts index 76fabfdd..6a197ea7 100644 --- a/src/queries.ts +++ b/src/queries.ts @@ -5,8 +5,8 @@ import { Conf } from '@/config.ts'; import { Storages } from '@/storages.ts'; import { type DittoEvent } from '@/interfaces/DittoEvent.ts'; import { type DittoRelation } from '@/interfaces/DittoFilter.ts'; -import { findReplyTag, getTagSet } from '@/tags.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; +import { findReplyTag, getTagSet } from '@/utils/tags.ts'; const debug = Debug('ditto:queries'); diff --git a/src/stats.ts b/src/stats.ts index 256c570e..6ffe5f7e 100644 --- a/src/stats.ts +++ b/src/stats.ts @@ -8,7 +8,7 @@ import { SetRequired } from 'type-fest'; import { DittoDB } from '@/db/DittoDB.ts'; import { DittoTables } from '@/db/DittoTables.ts'; import { Storages } from '@/storages.ts'; -import { findReplyTag, getTagSet } from '@/tags.ts'; +import { findReplyTag, getTagSet } from '@/utils/tags.ts'; type AuthorStat = keyof Omit; type EventStat = keyof Omit; diff --git a/src/storages/EventsDB.ts b/src/storages/EventsDB.ts index 5a3839a5..a550f39b 100644 --- a/src/storages/EventsDB.ts +++ b/src/storages/EventsDB.ts @@ -7,11 +7,11 @@ import { Kysely } from 'kysely'; import { Conf } from '@/config.ts'; import { DittoTables } from '@/db/DittoTables.ts'; import { normalizeFilters } from '@/filter.ts'; +import { RelayError } from '@/RelayError.ts'; import { purifyEvent } from '@/storages/hydrate.ts'; -import { getTagSet } from '@/tags.ts'; import { isNostrId, isURL } from '@/utils.ts'; import { abortError } from '@/utils/abort.ts'; -import { RelayError } from '@/RelayError.ts'; +import { getTagSet } from '@/utils/tags.ts'; /** Function to decide whether or not to index a tag. */ type TagCondition = ({ event, count, value }: { diff --git a/src/storages/UserStore.ts b/src/storages/UserStore.ts index c5657b6e..43c1771b 100644 --- a/src/storages/UserStore.ts +++ b/src/storages/UserStore.ts @@ -1,7 +1,7 @@ import { NostrEvent, NostrFilter, NStore } from '@nostrify/nostrify'; import { DittoEvent } from '@/interfaces/DittoEvent.ts'; -import { getTagSet } from '@/tags.ts'; +import { getTagSet } from '@/utils/tags.ts'; export class UserStore implements NStore { constructor(private pubkey: string, private store: NStore) {} diff --git a/src/storages/hydrate.ts b/src/storages/hydrate.ts index f55d7414..68dc0bdb 100644 --- a/src/storages/hydrate.ts +++ b/src/storages/hydrate.ts @@ -6,7 +6,7 @@ import { type DittoEvent } from '@/interfaces/DittoEvent.ts'; import { DittoTables } from '@/db/DittoTables.ts'; import { Conf } from '@/config.ts'; import { refreshAuthorStatsDebounced } from '@/stats.ts'; -import { findQuoteTag } from '@/tags.ts'; +import { findQuoteTag } from '@/utils/tags.ts'; interface HydrateOpts { events: DittoEvent[]; diff --git a/src/tags.ts b/src/utils/tags.ts similarity index 100% rename from src/tags.ts rename to src/utils/tags.ts diff --git a/src/views/mastodon/relationships.ts b/src/views/mastodon/relationships.ts index 2f8ffdde..425ea563 100644 --- a/src/views/mastodon/relationships.ts +++ b/src/views/mastodon/relationships.ts @@ -1,5 +1,5 @@ import { Storages } from '@/storages.ts'; -import { hasTag } from '@/tags.ts'; +import { hasTag } from '@/utils/tags.ts'; async function renderRelationship(sourcePubkey: string, targetPubkey: string) { const db = await Storages.db(); diff --git a/src/views/mastodon/statuses.ts b/src/views/mastodon/statuses.ts index 15719778..cc7cc36b 100644 --- a/src/views/mastodon/statuses.ts +++ b/src/views/mastodon/statuses.ts @@ -4,9 +4,9 @@ import { nip19 } from 'nostr-tools'; import { Conf } from '@/config.ts'; import { type DittoEvent } from '@/interfaces/DittoEvent.ts'; import { Storages } from '@/storages.ts'; -import { findQuoteTag, findReplyTag } from '@/tags.ts'; import { nostrDate } from '@/utils.ts'; import { getMediaLinks, parseNoteContent, stripimeta } from '@/utils/note.ts'; +import { findQuoteTag, findReplyTag } from '@/utils/tags.ts'; import { unfurlCardCached } from '@/utils/unfurl.ts'; import { accountFromPubkey, renderAccount } from '@/views/mastodon/accounts.ts'; import { renderAttachment } from '@/views/mastodon/attachments.ts'; From 5e607f664e2009ed8d25172609af6ad54caf8b0b Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 21 May 2024 13:32:27 -0500 Subject: [PATCH 3/4] Add tags test --- src/utils/tags.test.ts | 45 ++++++++++++++++++++++++++++++++++++++++++ src/utils/tags.ts | 36 ++++++++++++++++++++++++++------- 2 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 src/utils/tags.test.ts diff --git a/src/utils/tags.test.ts b/src/utils/tags.test.ts new file mode 100644 index 00000000..43e12359 --- /dev/null +++ b/src/utils/tags.test.ts @@ -0,0 +1,45 @@ +import { assertEquals } from '@std/assert'; + +import { addTag, deleteTag, findQuoteTag, findReplyTag, getTagSet, hasTag } from './tags.ts'; + +Deno.test('addTag', () => { + const tags = [['p', 'alex']]; + assertEquals(addTag(tags, ['p', 'alex']), [['p', 'alex']]); + assertEquals(addTag(tags, ['p', 'fiatjaf']), [['p', 'alex'], ['p', 'fiatjaf']]); +}); + +Deno.test('deleteTag', () => { + const tags = [['p', 'alex'], ['p', 'fiatjaf']]; + assertEquals(deleteTag(tags, ['p', 'alex']), [['p', 'fiatjaf']]); + assertEquals(deleteTag(tags, ['p', 'fiatjaf']), [['p', 'alex']]); +}); + +Deno.test('findQuoteTag', () => { + assertEquals(findQuoteTag([['q', '123']]), ['q', '123']); + assertEquals(findQuoteTag([['e', '', '', 'mention', '456']]), ['e', '', '', 'mention', '456']); + assertEquals(findQuoteTag([['e', '', '', 'mention', '456'], ['q', '123']]), ['q', '123']); + assertEquals(findQuoteTag([['q', '123'], ['e', '', '', 'mention', '456']]), ['q', '123']); +}); + +Deno.test('findReplyTag', () => { + const root = ['e', '123', '', 'root']; + const reply = ['e', '456', '', 'reply']; + + assertEquals(findReplyTag([root]), root); + assertEquals(findReplyTag([reply]), reply); + assertEquals(findReplyTag([root, reply]), reply); + assertEquals(findReplyTag([reply, root]), reply); + assertEquals(findReplyTag([['e', '321'], ['e', '789']]), ['e', '789']); + assertEquals(findReplyTag([reply, ['e', '789']]), reply); +}); + +Deno.test('getTagSet', () => { + const tags = [['p', 'alex'], ['p', 'fiatjaf'], ['p', 'alex']]; + assertEquals(getTagSet(tags, 'p'), new Set(['alex', 'fiatjaf'])); +}); + +Deno.test('hasTag', () => { + const tags = [['p', 'alex']]; + assertEquals(hasTag(tags, ['p', 'alex']), true); + assertEquals(hasTag(tags, ['p', 'fiatjaf']), false); +}); diff --git a/src/utils/tags.ts b/src/utils/tags.ts index ecddaf4b..6375e815 100644 --- a/src/utils/tags.ts +++ b/src/utils/tags.ts @@ -31,18 +31,40 @@ function addTag(tags: readonly string[][], tag: string[]): string[][] { } } -const isReplyTag = (tag: string[]) => tag[0] === 'e' && tag[3] === 'reply'; -const isRootTag = (tag: string[]) => tag[0] === 'e' && tag[3] === 'root'; -const isLegacyReplyTag = (tag: string[]) => tag[0] === 'e' && !tag[3]; +/** Tag is a NIP-10 root tag. */ +function isRootTag(tag: string[]): tag is ['e', string, string, 'root', ...string[]] { + return tag[0] === 'e' && tag[3] === 'root'; +} -const isQuoteTag = (tag: string[]) => tag[0] === 'q'; -const isLegacyQuoteTag = (tag: string[]) => tag[0] === 'e' && tag[3] === 'mention'; +/** Tag is a NIP-10 reply tag. */ +function isReplyTag(tag: string[]): tag is ['e', string, string, 'reply', ...string[]] { + return tag[0] === 'e' && tag[3] === 'reply'; +} -function findReplyTag(tags: string[][]): string[] | undefined { +/** Tag is a legacy "e" tag with a "mention" marker. */ +function isLegacyQuoteTag(tag: string[]): tag is ['e', string, string, 'mention', ...string[]] { + return tag[0] === 'e' && tag[3] === 'mention'; +} + +/** Tag is an "e" tag without a NIP-10 marker. */ +function isLegacyReplyTag(tag: string[]): tag is ['e', string, string] { + return tag[0] === 'e' && !tag[3]; +} + +/** Tag is a "q" tag. */ +function isQuoteTag(tag: string[]): tag is ['q', ...string[]] { + return tag[0] === 'q'; +} + +/** Get the "e" tag for the event being replied to, first according to the NIPs then falling back to the legacy way. */ +function findReplyTag(tags: string[][]): ['e', ...string[]] | undefined { return tags.find(isReplyTag) || tags.find(isRootTag) || tags.findLast(isLegacyReplyTag); } -function findQuoteTag(tags: string[][]): string[] | undefined { +/** Get the "q" tag, falling back to the legacy "e" tag with a "mention" marker. */ +function findQuoteTag( + tags: string[][], +): ['q', ...string[]] | ['e', string, string, 'mention', ...string[]] | undefined { return tags.find(isQuoteTag) || tags.find(isLegacyQuoteTag); } From 9873afab699b6c6ef24ce253abcf38e668bcbd7f Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 21 May 2024 13:37:24 -0500 Subject: [PATCH 4/4] Remove old tags.test.ts --- src/tags.test.ts | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/tags.test.ts diff --git a/src/tags.test.ts b/src/tags.test.ts deleted file mode 100644 index e49d31ab..00000000 --- a/src/tags.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { assertEquals } from '@std/assert'; - -import { addTag, deleteTag, getTagSet } from './tags.ts'; - -Deno.test('getTagSet', () => { - assertEquals(getTagSet([], 'p'), new Set()); - assertEquals(getTagSet([['p', '123']], 'p'), new Set(['123'])); - assertEquals(getTagSet([['p', '123'], ['p', '456']], 'p'), new Set(['123', '456'])); - assertEquals(getTagSet([['p', '123'], ['p', '456'], ['q', '789']], 'p'), new Set(['123', '456'])); -}); - -Deno.test('addTag', () => { - assertEquals(addTag([], ['p', '123']), [['p', '123']]); - assertEquals(addTag([['p', '123']], ['p', '123']), [['p', '123']]); - assertEquals(addTag([['p', '123'], ['p', '456']], ['p', '123']), [['p', '123'], ['p', '456']]); - assertEquals(addTag([['p', '123'], ['p', '456']], ['p', '789']), [['p', '123'], ['p', '456'], ['p', '789']]); -}); - -Deno.test('deleteTag', () => { - assertEquals(deleteTag([], ['p', '123']), []); - assertEquals(deleteTag([['p', '123']], ['p', '123']), []); - assertEquals(deleteTag([['p', '123']], ['p', '456']), [['p', '123']]); - assertEquals(deleteTag([['p', '123'], ['p', '123']], ['p', '123']), []); - assertEquals(deleteTag([['p', '123'], ['p', '456']], ['p', '456']), [['p', '123']]); -});