Poptavka z webu je ticket, prilohy, udaje provozovatele, kolacovy graf
Provozovatel portalu: firma s priznakem portalOperator (jen jedna, zapnuti odebere ostatnim) a novymi poli contactEmail, contactPhone, website vedle ico, dic, adresy a pravni formy. Verejny GET /api/public/brand vraci jeji udaje a web je bere pres useBrand() na kontaktu, v paticce, O nas, prihlaseni i v titulku; brand.ts je jen zaloha. Poptavka z webu zaklada u provozovatele ticket kanalu form: predmet "Poptavka: tema", telo JSON s poli formulare, tag Poptavka plus tema, zakaznik z formulare, poznamka v logu. Bez provozovatele se jen zaloguje. Prilohy ticketu: formular az 3 soubory po 5 MB, ticket az 10; nahrani, seznam, stazeni a smazani (pravo ticket.comment, strop viditelnosti, poznamky v logu, audit). Soubor jde v JSON jako Base64 a lezi v beznem ulozisti, bez nove zavislosti; strop tela jen na techto cestach. Vlastni widget s kreslenim Graf umi i pocet ticketu se seskupenim jako kolac (PieChart.tsx, ciste SVG, osm barev z tokenu, zbytek jako ostatni). OpenAPI rozdelene na mensi soubory (102 cest, 28 schemat overeno shodnych), 28 novych testu (135 celkem), dokumentace aktualizovana.
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Prilohy ticketu (src/data/attachments.ts): limity, ocisteni nazvu, base64,
|
||||
* seznam bez obsahu a filtr na firmu. Uloziste v pameti.
|
||||
*/
|
||||
|
||||
import { beforeAll, describe, expect, test } from 'vitest';
|
||||
import {
|
||||
addAttachments,
|
||||
attachmentStore,
|
||||
checkUploads,
|
||||
getAttachment,
|
||||
listAttachments,
|
||||
MAX_ATTACHMENT_BYTES,
|
||||
MAX_ATTACHMENTS_PER_TICKET,
|
||||
removeAttachment,
|
||||
sanitizeName,
|
||||
} from '../../src/data/attachments.js';
|
||||
import { initStores } from '../../src/data/store/index.js';
|
||||
import { createTicket, getTicket, initTickets, type Ticket } from '../../src/data/ticketStore.js';
|
||||
import { refreshUsers, seedUsers, userStore } from '../../src/data/users.js';
|
||||
|
||||
let ticket: Ticket;
|
||||
|
||||
function upload(name: string, bytes: number, mime?: string) {
|
||||
return { name, mime, content: Buffer.alloc(bytes, 1).toString('base64') };
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
initStores({ databaseReady: false });
|
||||
await userStore.init(seedUsers);
|
||||
await refreshUsers();
|
||||
await attachmentStore.init();
|
||||
await initTickets();
|
||||
ticket = createTicket({
|
||||
tenantId: 'tnt_automia',
|
||||
subject: 'Ticket s prilohami',
|
||||
channel: 'portal',
|
||||
priority: 'normal',
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkUploads', () => {
|
||||
test('soubor nad limit velikosti neprojde', () => {
|
||||
const result = checkUploads([upload('velky.bin', MAX_ATTACHMENT_BYTES + 1)], 0);
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) expect(result.message).toContain('velky.bin');
|
||||
});
|
||||
|
||||
test('soubor presne na limitu projde', () => {
|
||||
expect(checkUploads([upload('hranice.bin', MAX_ATTACHMENT_BYTES)], 0).ok).toBe(true);
|
||||
});
|
||||
|
||||
test('pocet priloh na ticket ma strop', () => {
|
||||
const result = checkUploads([upload('dalsi.txt', 10)], MAX_ATTACHMENTS_PER_TICKET);
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) expect(result.message).toContain(String(MAX_ATTACHMENTS_PER_TICKET));
|
||||
});
|
||||
|
||||
test('neplatny base64 a prazdny obsah se odmitnou', () => {
|
||||
expect(checkUploads([{ name: 'a.txt', content: 'to neni base64!' }], 0).ok).toBe(false);
|
||||
expect(checkUploads([{ name: 'a.txt', content: 'abc' }], 0).ok).toBe(false);
|
||||
expect(checkUploads([{ name: 'a.txt', content: '' }], 0).ok).toBe(false);
|
||||
});
|
||||
|
||||
test('neznamy typ obsahu spadne na octet-stream', () => {
|
||||
const result = checkUploads([upload('a.txt', 3, 'nesmysl bez lomitka')], 0);
|
||||
expect(result.ok).toBe(true);
|
||||
if (result.ok) expect(result.files[0]?.mime).toBe('application/octet-stream');
|
||||
});
|
||||
});
|
||||
|
||||
describe('sanitizeName', () => {
|
||||
test('zahodi cestu a ridici znaky', () => {
|
||||
expect(sanitizeName('../../etc/passwd')).toBe('passwd');
|
||||
expect(sanitizeName('C:\\Users\\x\\zprava.pdf')).toBe('zprava.pdf');
|
||||
expect(sanitizeName('a\r\nb\u0000c.txt')).toBe('abc.txt');
|
||||
});
|
||||
|
||||
test('prazdny nazev dostane nahradu a dlouhy se zkrati', () => {
|
||||
expect(sanitizeName('/')).toBe('priloha');
|
||||
expect(sanitizeName('..')).toBe('priloha');
|
||||
expect(sanitizeName('x'.repeat(500)).length).toBe(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addAttachments a listAttachments', () => {
|
||||
test('ulozi prilohy, seznam je bez obsahu a ticket dostane radek do logu', async () => {
|
||||
const result = await addAttachments(
|
||||
ticket,
|
||||
[upload('zadani.pdf', 1500, 'application/pdf'), upload('foto.jpg', 20, 'image/jpeg')],
|
||||
'usr_1',
|
||||
);
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
expect(result.items).toHaveLength(2);
|
||||
expect(result.items[0]).toMatchObject({
|
||||
ticketId: ticket.id,
|
||||
tenantId: 'tnt_automia',
|
||||
name: 'zadani.pdf',
|
||||
mime: 'application/pdf',
|
||||
size: 1500,
|
||||
uploadedBy: 'usr_1',
|
||||
});
|
||||
expect('content' in (result.items[0] as object)).toBe(false);
|
||||
|
||||
const items = await listAttachments(ticket.id, ['tnt_automia']);
|
||||
expect(items).toHaveLength(2);
|
||||
for (const item of items) expect('content' in (item as object)).toBe(false);
|
||||
|
||||
const detail = getTicket(ticket.id, ['tnt_automia']);
|
||||
const labels = detail?.trace.map((entry) => entry.label) ?? [];
|
||||
expect(labels).toContain('Příloha: zadani.pdf (1.5 kB)');
|
||||
});
|
||||
|
||||
test('cizi firma nevidi nic a nedostane obsah', async () => {
|
||||
expect(await listAttachments(ticket.id, ['tnt_nordis'])).toEqual([]);
|
||||
const [first] = await listAttachments(ticket.id, ['tnt_automia']);
|
||||
expect(await getAttachment(first!.id, ticket.id, ['tnt_nordis'])).toBeUndefined();
|
||||
expect(await removeAttachment(first!.id, ticket.id, ['tnt_nordis'])).toBe(false);
|
||||
});
|
||||
|
||||
test('obsah se vraci jen pres getAttachment a odpovida ulozenemu', async () => {
|
||||
const [first] = await listAttachments(ticket.id, ['tnt_automia']);
|
||||
const full = await getAttachment(first!.id, ticket.id, ['tnt_automia']);
|
||||
expect(full?.content).toBe(Buffer.alloc(1500, 1).toString('base64'));
|
||||
// Jiny ticket se stejnym ID prilohy je taky "neexistuje".
|
||||
expect(await getAttachment(first!.id, 'TK-0', ['tnt_automia'])).toBeUndefined();
|
||||
});
|
||||
|
||||
test('davka nad strop poctu se neulozi ani castecne', async () => {
|
||||
const before = (await listAttachments(ticket.id, ['tnt_automia'])).length;
|
||||
const many = Array.from({ length: MAX_ATTACHMENTS_PER_TICKET }, (_, i) =>
|
||||
upload(`f${i}.txt`, 4),
|
||||
);
|
||||
const result = await addAttachments(ticket, many, 'usr_1');
|
||||
expect(result.ok).toBe(false);
|
||||
expect(await listAttachments(ticket.id, ['tnt_automia'])).toHaveLength(before);
|
||||
});
|
||||
|
||||
test('odebrani prilohy zapise poznamku do logu', async () => {
|
||||
const [first] = await listAttachments(ticket.id, ['tnt_automia']);
|
||||
expect(await removeAttachment(first!.id, ticket.id, ['tnt_automia'])).toBe(true);
|
||||
expect(await listAttachments(ticket.id, ['tnt_automia'])).toHaveLength(1);
|
||||
const labels = getTicket(ticket.id, ['tnt_automia'])?.trace.map((entry) => entry.label) ?? [];
|
||||
expect(labels).toContain('Příloha odebrána: zadani.pdf');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { validateWidget, type CustomWidget } from '../../src/data/customWidgets.js';
|
||||
|
||||
/** Widget se zakladem, ktery validace pousti; testy prepisuji jen to, o co jde. */
|
||||
function widget(overrides: Partial<CustomWidget>): CustomWidget {
|
||||
const now = '2026-09-09T00:00:00.000Z';
|
||||
return {
|
||||
id: 'cw_test',
|
||||
tenantId: 'tnt_automia',
|
||||
ownerId: null,
|
||||
name: 'Test',
|
||||
description: '',
|
||||
render: 'chart',
|
||||
size: 'half',
|
||||
source: { kind: 'ticketSeries', filter: {}, bucket: 'day' },
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe('validateWidget: graf', () => {
|
||||
test('casova rada projde', () => {
|
||||
expect(validateWidget(widget({}))).toEqual([]);
|
||||
});
|
||||
|
||||
test('pocet ticketu se seskupenim projde jako kolac', () => {
|
||||
const result = validateWidget(
|
||||
widget({ source: { kind: 'ticketCount', filter: { closed: false }, groupBy: 'status' } }),
|
||||
);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
test('pocet ticketu bez seskupeni je pro graf chyba', () => {
|
||||
const result = validateWidget(widget({ source: { kind: 'ticketCount', filter: {} } }));
|
||||
expect(result.some((problem) => problem.includes('Graf'))).toBe(true);
|
||||
});
|
||||
|
||||
test('seznam ticketu se jako graf nekresli', () => {
|
||||
const result = validateWidget(
|
||||
widget({ source: { kind: 'ticketList', filter: {}, limit: 5 } }),
|
||||
);
|
||||
expect(result.some((problem) => problem.includes('Graf'))).toBe(true);
|
||||
});
|
||||
|
||||
test('graf ve tretine sirky neprojde', () => {
|
||||
const result = validateWidget(widget({ size: 'third' }));
|
||||
expect(result.some((problem) => problem.includes('Šířka'))).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Prilohy ticketu pres HTTP (src/routes/dashboard/attachments.ts): nahrani
|
||||
* jako resitel s ticket.comment, seznam, stazeni, smazani, cizi firma 404,
|
||||
* prilis velky soubor 400.
|
||||
*/
|
||||
|
||||
import request from 'supertest';
|
||||
import { beforeAll, describe, expect, test } from 'vitest';
|
||||
import type { Express } from 'express';
|
||||
import { createApp } from '../../src/app.js';
|
||||
import { MAX_ATTACHMENT_BYTES } from '../../src/data/attachments.js';
|
||||
import { bootstrapData } from '../../src/data/bootstrap.js';
|
||||
import { createTicket, type Ticket } from '../../src/data/ticketStore.js';
|
||||
|
||||
let app: Express;
|
||||
let agentToken: string;
|
||||
let foreignToken: string;
|
||||
let ticket: Ticket;
|
||||
const payload = Buffer.from('obsah prilohy s diakritikou: příloha');
|
||||
|
||||
async function login(email: string): Promise<string> {
|
||||
const response = await request(app).post('/api/auth/login').send({ email, password: 'demo1234' });
|
||||
expect(response.status).toBe(200);
|
||||
return response.body.token as string;
|
||||
}
|
||||
|
||||
function base(): string {
|
||||
return `/api/dashboard/tickets/${ticket.id}/attachments`;
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
await bootstrapData({ databaseReady: false });
|
||||
app = createApp();
|
||||
// Karel je v Automii agent (ma ticket.comment), Ondrej je jen v LogiTransu.
|
||||
agentToken = await login('karel.vomacka@automia.cz');
|
||||
foreignToken = await login('ondrej.kadlec@logitrans.cz');
|
||||
ticket = createTicket({
|
||||
tenantId: 'tnt_automia',
|
||||
subject: 'Ticket pro prilohy',
|
||||
channel: 'portal',
|
||||
priority: 'normal',
|
||||
});
|
||||
});
|
||||
|
||||
describe('prilohy ticketu', () => {
|
||||
let attachmentId: string;
|
||||
|
||||
test('nahrani vrati 201 a jen nove pridane prilohy', async () => {
|
||||
const response = await request(app)
|
||||
.post(base())
|
||||
.set('Authorization', `Bearer ${agentToken}`)
|
||||
.send({
|
||||
files: [{ name: 'zadání.txt', mime: 'text/plain', content: payload.toString('base64') }],
|
||||
});
|
||||
expect(response.status).toBe(201);
|
||||
expect(response.body.items).toHaveLength(1);
|
||||
expect(response.body.items[0]).toMatchObject({
|
||||
ticketId: ticket.id,
|
||||
name: 'zadání.txt',
|
||||
mime: 'text/plain',
|
||||
size: payload.length,
|
||||
uploadedBy: 'usr_2',
|
||||
});
|
||||
expect(response.body.items[0].content).toBeUndefined();
|
||||
attachmentId = response.body.items[0].id as string;
|
||||
});
|
||||
|
||||
test('seznam vrati prilohu bez obsahu', async () => {
|
||||
const response = await request(app).get(base()).set('Authorization', `Bearer ${agentToken}`);
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.items).toHaveLength(1);
|
||||
expect(response.body.items[0].id).toBe(attachmentId);
|
||||
expect(response.body.items[0].content).toBeUndefined();
|
||||
});
|
||||
|
||||
test('stazeni vrati stejne bajty a hlavicky', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${base()}/${attachmentId}/content`)
|
||||
.set('Authorization', `Bearer ${agentToken}`)
|
||||
.buffer(true)
|
||||
.parse((res, callback) => {
|
||||
const chunks: Buffer[] = [];
|
||||
res.on('data', (chunk: Buffer) => chunks.push(chunk));
|
||||
res.on('end', () => callback(null, Buffer.concat(chunks)));
|
||||
});
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers['content-type']).toBe('text/plain');
|
||||
expect(response.headers['content-length']).toBe(String(payload.length));
|
||||
expect(response.headers['content-disposition']).toBe(
|
||||
`attachment; filename*=UTF-8''${encodeURIComponent('zadání.txt')}`,
|
||||
);
|
||||
expect(Buffer.compare(response.body as Buffer, payload)).toBe(0);
|
||||
});
|
||||
|
||||
test('cizi firma dostane 404 na seznam, nahrani i stazeni', async () => {
|
||||
const auth = `Bearer ${foreignToken}`;
|
||||
expect((await request(app).get(base()).set('Authorization', auth)).status).toBe(404);
|
||||
expect(
|
||||
(
|
||||
await request(app)
|
||||
.post(base())
|
||||
.set('Authorization', auth)
|
||||
.send({ files: [{ name: 'x.txt', content: 'YWJj' }] })
|
||||
).status,
|
||||
).toBe(404);
|
||||
expect(
|
||||
(await request(app).get(`${base()}/${attachmentId}/content`).set('Authorization', auth))
|
||||
.status,
|
||||
).toBe(404);
|
||||
});
|
||||
|
||||
test('soubor nad limit je 400', async () => {
|
||||
const response = await request(app)
|
||||
.post(base())
|
||||
.set('Authorization', `Bearer ${agentToken}`)
|
||||
.send({
|
||||
files: [
|
||||
{ name: 'velky.bin', content: Buffer.alloc(MAX_ATTACHMENT_BYTES + 1).toString('base64') },
|
||||
],
|
||||
});
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body.error).toBe('validation_error');
|
||||
});
|
||||
|
||||
test('smazani vrati 204 a priloha zmizi', async () => {
|
||||
const auth = `Bearer ${agentToken}`;
|
||||
expect(
|
||||
(await request(app).delete(`${base()}/${attachmentId}`).set('Authorization', auth)).status,
|
||||
).toBe(204);
|
||||
const list = await request(app).get(base()).set('Authorization', auth);
|
||||
expect(list.body.items).toHaveLength(0);
|
||||
expect(
|
||||
(await request(app).delete(`${base()}/${attachmentId}`).set('Authorization', auth)).status,
|
||||
).toBe(404);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Kontaktni formular (src/routes/contact.ts): s provozovatelem portalu vznika
|
||||
* ticket s prilohou, bez nej jen 202, limit pokusu zustava.
|
||||
*
|
||||
* Limit je na adresu a supertest vola vzdy z te same, proto test pocita
|
||||
* odeslane pokusy a na konci overi, ze sesty je 429.
|
||||
*/
|
||||
|
||||
import request from 'supertest';
|
||||
import { beforeAll, describe, expect, test } from 'vitest';
|
||||
import type { Express } from 'express';
|
||||
import { createApp } from '../../src/app.js';
|
||||
import { listAttachments } from '../../src/data/attachments.js';
|
||||
import { bootstrapData } from '../../src/data/bootstrap.js';
|
||||
import { refreshTenants, tenantStore } from '../../src/data/tenants.js';
|
||||
import { listTickets } from '../../src/data/ticketStore.js';
|
||||
|
||||
let app: Express;
|
||||
/** Kolik pokusu uz slo z teto adresy. Limit je 5 za hodinu. */
|
||||
let attempts = 0;
|
||||
const CONTACT_MAX_PER_WINDOW = 5;
|
||||
|
||||
const form = {
|
||||
name: 'Petra Klientová',
|
||||
email: 'petra@example.com',
|
||||
company: 'Klient s.r.o.',
|
||||
phone: '+420 777 000 111',
|
||||
topic: 'integrace',
|
||||
message: 'Potřebujeme napojit e-shop na iDoklad, prosím o nabídku.',
|
||||
};
|
||||
|
||||
async function setOperator(portalOperator: boolean): Promise<void> {
|
||||
await tenantStore.update(
|
||||
'tnt_automia',
|
||||
{ portalOperator },
|
||||
{ tenantIds: [], includeGlobal: true },
|
||||
);
|
||||
await refreshTenants();
|
||||
}
|
||||
|
||||
function send(body: Record<string, unknown>): request.Test {
|
||||
attempts += 1;
|
||||
return request(app).post('/api/contact').send(body);
|
||||
}
|
||||
|
||||
function ticketsOfAutomia() {
|
||||
return listTickets({ tenantIds: ['tnt_automia'], visibility: { kind: 'all' } });
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
await bootstrapData({ databaseReady: false });
|
||||
app = createApp();
|
||||
});
|
||||
|
||||
describe('POST /api/contact', () => {
|
||||
test('s provozovatelem zalozi ticket z formulare vcetne prilohy', async () => {
|
||||
await setOperator(true);
|
||||
const content = Buffer.from('ukazkovy obsah').toString('base64');
|
||||
const response = await send({
|
||||
...form,
|
||||
attachments: [{ name: 'zadani.txt', mime: 'text/plain', content }],
|
||||
});
|
||||
expect(response.status).toBe(202);
|
||||
expect(response.body.ok).toBe(true);
|
||||
|
||||
const [ticket] = ticketsOfAutomia();
|
||||
expect(ticket).toBeDefined();
|
||||
expect(ticket!.channel).toBe('form');
|
||||
expect(ticket!.subject).toBe('Poptávka: integrace');
|
||||
expect(ticket!.tags).toEqual(['Poptávka', 'integrace']);
|
||||
expect(ticket!.externalSource).toBe('web-form');
|
||||
expect(ticket!.assignee).toBeNull();
|
||||
expect(ticket!.customer).toEqual({
|
||||
id: null,
|
||||
company: 'Klient s.r.o.',
|
||||
contact: 'Petra Klientová',
|
||||
reply: 'petra@example.com',
|
||||
});
|
||||
|
||||
const body = JSON.parse(ticket!.body) as Record<string, unknown>;
|
||||
expect(body).toMatchObject({ ...form });
|
||||
expect(typeof body.receivedAt).toBe('string');
|
||||
|
||||
const attachments = await listAttachments(ticket!.id, ['tnt_automia']);
|
||||
expect(attachments).toHaveLength(1);
|
||||
expect(attachments[0]).toMatchObject({
|
||||
name: 'zadani.txt',
|
||||
mime: 'text/plain',
|
||||
uploadedBy: null,
|
||||
});
|
||||
});
|
||||
|
||||
test('neplatna priloha je 400 a ticket nevznikne', async () => {
|
||||
const before = ticketsOfAutomia().length;
|
||||
const response = await send({
|
||||
...form,
|
||||
attachments: [{ name: 'x.txt', content: 'to neni base64!' }],
|
||||
});
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body.error).toBe('validation_error');
|
||||
expect(ticketsOfAutomia()).toHaveLength(before);
|
||||
});
|
||||
|
||||
test('bez provozovatele je 202 a zadny ticket', async () => {
|
||||
await setOperator(false);
|
||||
const before = ticketsOfAutomia().length;
|
||||
const response = await send(form);
|
||||
expect(response.status).toBe(202);
|
||||
expect(ticketsOfAutomia()).toHaveLength(before);
|
||||
});
|
||||
|
||||
test('limit pokusu z jedne adresy zustava', async () => {
|
||||
while (attempts < CONTACT_MAX_PER_WINDOW) {
|
||||
expect((await send(form)).status).toBe(202);
|
||||
}
|
||||
const blocked = await send(form);
|
||||
expect(blocked.status).toBe(429);
|
||||
expect(blocked.headers['retry-after']).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Verejne udaje provozovatele (src/routes/public.ts): s provozovatelem jsou
|
||||
* pole vyplnena, bez nej same null a porad 200.
|
||||
*/
|
||||
|
||||
import request from 'supertest';
|
||||
import { beforeAll, describe, expect, test } from 'vitest';
|
||||
import type { Express } from 'express';
|
||||
import { createApp } from '../../src/app.js';
|
||||
import { bootstrapData } from '../../src/data/bootstrap.js';
|
||||
import { refreshTenants, tenantStore } from '../../src/data/tenants.js';
|
||||
|
||||
let app: Express;
|
||||
|
||||
beforeAll(async () => {
|
||||
await bootstrapData({ databaseReady: false });
|
||||
app = createApp();
|
||||
});
|
||||
|
||||
describe('GET /api/public/brand', () => {
|
||||
test('bez provozovatele jsou vsechna pole null', async () => {
|
||||
const response = await request(app).get('/api/public/brand');
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers['cache-control']).toBe('public, max-age=60');
|
||||
expect(response.body).toEqual({
|
||||
name: null,
|
||||
legalName: null,
|
||||
ico: null,
|
||||
dic: null,
|
||||
address: null,
|
||||
legalForm: null,
|
||||
email: null,
|
||||
phone: null,
|
||||
website: null,
|
||||
});
|
||||
});
|
||||
|
||||
test('s provozovatelem vraci jeho udaje', async () => {
|
||||
await tenantStore.update(
|
||||
'tnt_automia',
|
||||
{
|
||||
portalOperator: true,
|
||||
ico: '12345678',
|
||||
dic: 'CZ12345678',
|
||||
address: 'Náměstí Míru 12, Brno',
|
||||
legalForm: 'Společnost s ručením omezeným',
|
||||
contactEmail: 'info@automia.cz',
|
||||
contactPhone: '+420 777 123 456',
|
||||
website: 'https://www.automia.cz',
|
||||
},
|
||||
{ tenantIds: [], includeGlobal: true },
|
||||
);
|
||||
await refreshTenants();
|
||||
|
||||
const response = await request(app).get('/api/public/brand');
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toEqual({
|
||||
name: 'Automia',
|
||||
legalName: 'Automia',
|
||||
ico: '12345678',
|
||||
dic: 'CZ12345678',
|
||||
address: 'Náměstí Míru 12, Brno',
|
||||
legalForm: 'Společnost s ručením omezeným',
|
||||
email: 'info@automia.cz',
|
||||
phone: '+420 777 123 456',
|
||||
website: 'https://www.automia.cz',
|
||||
});
|
||||
});
|
||||
|
||||
test('spravce platformy prepne provozovatele a puvodnimu priznak zmizi', async () => {
|
||||
const login = await request(app)
|
||||
.post('/api/auth/login')
|
||||
.send({ email: 'admin@automia.cz', password: 'demo1234' });
|
||||
const response = await request(app)
|
||||
.patch('/api/dashboard/settings/tenants/tnt_nordis')
|
||||
.set('Authorization', `Bearer ${login.body.token}`)
|
||||
.send({ portalOperator: true, contactEmail: '' });
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.portalOperator).toBe(true);
|
||||
expect(response.body.contactEmail).toBeNull();
|
||||
|
||||
const automia = await tenantStore.getRaw('tnt_automia');
|
||||
expect(automia?.portalOperator).toBe(false);
|
||||
const brand = await request(app).get('/api/public/brand');
|
||||
expect(brand.body.name).toBe('Nordis a.s.');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user