diff --git a/backend/src/graphql/resolver/CommunityResolver.test.ts b/backend/src/graphql/resolver/CommunityResolver.test.ts index b65cb65c0..5e7d929e2 100644 --- a/backend/src/graphql/resolver/CommunityResolver.test.ts +++ b/backend/src/graphql/resolver/CommunityResolver.test.ts @@ -16,10 +16,9 @@ import { reachableCommunities, } from '@/seeds/graphql/queries' import { peterLustig } from '@/seeds/users/peter-lustig' -import { createCommunity, createAuthenticatedForeignCommunity } from 'database/src/seeds/community' +import { createCommunity, createVerifiedFederatedCommunity } from 'database/src/seeds/community' import { getLogger } from 'config-schema/test/testSetup' -import { getCommunityByUuid } from './util/communities' import { CONFIG } from '@/config' jest.mock('@/password/EncryptorUtils') @@ -445,6 +444,7 @@ describe('CommunityResolver', () => { afterAll(async () => { await DbCommunity.clear() + await DbFederatedCommunity.clear() }) describe('with empty list', () => { @@ -483,33 +483,37 @@ describe('CommunityResolver', () => { describe('returns 2 filtered communities even with 3 existing entries', () => { beforeEach(async () => { foreignCom1 = await createCommunity(true, false) - foreignCom2 = await createAuthenticatedForeignCommunity(100, false) + foreignCom2 = await createCommunity(true, false) + const com1FedCom = await createVerifiedFederatedCommunity('1_0', 100, foreignCom1, false) + const com1FedCom2 = await createVerifiedFederatedCommunity('1_1', 100, foreignCom1, false) + const com2FedCom = await createVerifiedFederatedCommunity('1_0', 10000, foreignCom2, false) await Promise.all([ DbCommunity.insert(foreignCom1), - DbCommunity.insert(foreignCom2) + DbCommunity.insert(foreignCom2), + DbFederatedCommunity.insert(com1FedCom), + DbFederatedCommunity.insert(com1FedCom2), + DbFederatedCommunity.insert(com2FedCom) ]) }) it('returns 2 community entries', async () => { - await expect(query({ query: reachableCommunities })).resolves.toMatchObject({ - data: { - reachableCommunities: [ - { - foreign: homeCom1.foreign, - name: homeCom1.name, - description: homeCom1.description, - url: homeCom1.url, - uuid: homeCom1.communityUuid, - }, { - foreign: foreignCom2.foreign, - name: foreignCom2.name, - description: foreignCom2.description, - url: foreignCom2.url, - uuid: foreignCom2.communityUuid, - }, - ], - }, - }) + const result = await query({ query: reachableCommunities }) + expect(result.data.reachableCommunities.length).toBe(2) + expect(result.data.reachableCommunities).toMatchObject([ + { + foreign: homeCom1.foreign, + name: homeCom1.name, + description: homeCom1.description, + url: homeCom1.url, + uuid: homeCom1.communityUuid, + }, { + foreign: foreignCom1.foreign, + name: foreignCom1.name, + description: foreignCom1.description, + url: foreignCom1.url, + uuid: foreignCom1.communityUuid, + } + ]) }) }) diff --git a/config-schema/src/log4js-config/coloredContext.ts b/config-schema/src/log4js-config/coloredContext.ts index 1ebb6b219..1f238fcdb 100644 --- a/config-schema/src/log4js-config/coloredContext.ts +++ b/config-schema/src/log4js-config/coloredContext.ts @@ -33,7 +33,7 @@ function composeDataString(data: (string | Object)[]): string { return data .map((d) => { // if it is a object and his toString function return only garbage - if (typeof d === 'object' && d.toString() === '[object Object]') { + if (d && typeof d === 'object' && d.toString() === '[object Object]') { return inspect(d, ) } if (d) { diff --git a/database/src/queries/communities.test.ts b/database/src/queries/communities.test.ts index 62dedabf1..18975256c 100644 --- a/database/src/queries/communities.test.ts +++ b/database/src/queries/communities.test.ts @@ -1,8 +1,8 @@ -import { Community as DbCommunity } from '..' +import { Community as DbCommunity, FederatedCommunity as DbFederatedCommunity } from '..' import { AppDatabase } from '../AppDatabase' import { getHomeCommunity, getReachableCommunities } from './communities' import { describe, expect, it, beforeEach, beforeAll, afterAll } from 'vitest' -import { createCommunity, createAuthenticatedForeignCommunity } from '../seeds/community' +import { createCommunity, createVerifiedFederatedCommunity } from '../seeds/community' const db = AppDatabase.getInstance() @@ -17,6 +17,7 @@ describe('community.queries', () => { // clean db for every test case beforeEach(async () => { await DbCommunity.clear() + await DbFederatedCommunity.clear() }) describe('getHomeCommunity', () => { it('should return null if no home community exists', async () => { @@ -44,21 +45,44 @@ describe('community.queries', () => { expect(await getReachableCommunities(1000)).toHaveLength(1) }) it('foreign communities authenticated within chosen range', async () => { - await createAuthenticatedForeignCommunity(400) - await createAuthenticatedForeignCommunity(500) - await createAuthenticatedForeignCommunity(1200) + const com1 = await createCommunity(true) + const com2 = await createCommunity(true) + const com3 = await createCommunity(true) + await createVerifiedFederatedCommunity('1_0', 100, com1) + await createVerifiedFederatedCommunity('1_0', 500, com2) + // outside of range + await createVerifiedFederatedCommunity('1_0', 1200, com3) - const community = await getReachableCommunities(1000) - expect(community).toHaveLength(2) + const communities = await getReachableCommunities(1000) + expect(communities).toHaveLength(2) + expect(communities[0].communityUuid).toBe(com1.communityUuid) + expect(communities[1].communityUuid).toBe(com2.communityUuid) + }) + it('multiple federated community api version, result in one community', async () => { + const com1 = await createCommunity(true) + await createVerifiedFederatedCommunity('1_0', 100, com1) + await createVerifiedFederatedCommunity('1_1', 100, com1) + expect(await getReachableCommunities(1000)).toHaveLength(1) + }) + it('multiple federated community api version one outside of range, result in one community', async () => { + const com1 = await createCommunity(true) + await createVerifiedFederatedCommunity('1_0', 100, com1) + // outside of range + await createVerifiedFederatedCommunity('1_1', 1200, com1) + expect(await getReachableCommunities(1000)).toHaveLength(1) }) it('foreign and home community', async () => { + // home community await createCommunity(false) - await createAuthenticatedForeignCommunity(400) - await createAuthenticatedForeignCommunity(1200) + const com1 = await createCommunity(true) + const com2 = await createCommunity(true) + await createVerifiedFederatedCommunity('1_0', 400, com1) + await createVerifiedFederatedCommunity('1_0', 1200, com2) expect(await getReachableCommunities(1000)).toHaveLength(2) }) - it('not authenticated foreign community', async () => { - await createCommunity(true) + it('not verified inside time frame federated community', async () => { + const com1 = await createCommunity(true) + await createVerifiedFederatedCommunity('1_0', 1200, com1) expect(await getReachableCommunities(1000)).toHaveLength(0) }) }) diff --git a/database/src/queries/communities.ts b/database/src/queries/communities.ts index 5bf8798c8..e216f8af6 100644 --- a/database/src/queries/communities.ts +++ b/database/src/queries/communities.ts @@ -43,14 +43,19 @@ export async function getCommunityWithFederatedCommunityByIdentifier( } // returns all reachable communities -// home community and all foreign communities which have been authenticated within the last authenticationTimeoutMs +// home community and all federated communities which have been verified within the last authenticationTimeoutMs export async function getReachableCommunities( authenticationTimeoutMs: number, order?: FindOptionsOrder ): Promise { return await DbCommunity.find({ where: [ - { communityUuid: Not(IsNull()), authenticatedAt: MoreThanOrEqual(new Date(Date.now() - authenticationTimeoutMs)) }, + { + authenticatedAt: Not(IsNull()), + federatedCommunities: { + verifiedAt: MoreThanOrEqual(new Date(Date.now() - authenticationTimeoutMs)) + } + }, { foreign: false }, ], order, diff --git a/database/src/seeds/community.ts b/database/src/seeds/community.ts index b57e839e1..4db872398 100644 --- a/database/src/seeds/community.ts +++ b/database/src/seeds/community.ts @@ -1,4 +1,4 @@ -import { Community } from '../entity' +import { Community, FederatedCommunity } from '../entity' import { randomBytes } from 'node:crypto' import { v4 as uuidv4 } from 'uuid' @@ -14,8 +14,10 @@ export async function createCommunity(foreign: boolean, save: boolean = true): P community.name = 'ForeignCommunity-name' community.description = 'ForeignCommunity-description' community.url = `http://foreign-${Math.random()}/api` + community.authenticatedAt = new Date() } else { community.foreign = false + // todo: generate valid public/private key pair (ed25519) community.privateKey = randomBytes(64) community.name = 'HomeCommunity-name' community.description = 'HomeCommunity-description' @@ -24,11 +26,17 @@ export async function createCommunity(foreign: boolean, save: boolean = true): P return save ? await community.save() : community } -export async function createAuthenticatedForeignCommunity( - authenticatedBeforeMs: number, +export async function createVerifiedFederatedCommunity( + apiVersion: string, + verifiedBeforeMs: number, + community: Community, save: boolean = true -): Promise { - const foreignCom = await createCommunity(true, false) - foreignCom.authenticatedAt = new Date(Date.now() - authenticatedBeforeMs) - return save ? await foreignCom.save() : foreignCom -} \ No newline at end of file +): Promise { + const federatedCommunity = new FederatedCommunity() + federatedCommunity.apiVersion = apiVersion + federatedCommunity.endPoint = community.url + federatedCommunity.publicKey = community.publicKey + federatedCommunity.community = community + federatedCommunity.verifiedAt = new Date(Date.now() - verifiedBeforeMs) + return save ? await federatedCommunity.save() : federatedCommunity +}