File size: 12,976 Bytes
31226fd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dcd5bb9
 
31226fd
 
 
76af1af
31226fd
 
76af1af
31226fd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
850ee3d
31226fd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76af1af
31226fd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
990036c
31226fd
 
 
 
 
 
 
 
 
 
 
 
 
 
850ee3d
31226fd
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
import { test, expect } from '@playwright/test';
import { selectRoute, selectLabStage, selectResearchView } from './routes.js';

test.use({ baseURL: 'http://127.0.0.1:4174' });



for (const port of [4174, 4175]) {
  test(`real server ${port}: routing, catalog, MSBA and shared file/chat renderer`, async ({ page }) => {
    const errors = [];
    page.on('pageerror', error => errors.push(error.message));
    await page.goto(`http://127.0.0.1:${port}/`);
    await selectLabStage(page, 'question');
    await expect(page.locator('#workflow-research-results')).toBeVisible();
    await page.locator('#workflow-research-search').fill('RQ-MSBA-E01');
    await expect(page.locator('.research-rq-card')).toHaveCount(1);
    await page.locator('.research-rq-card').click();
    await expect(page.locator('#workflow-research-results')).toContainText('OPERATIONAL');
    await page.locator('#workflow-research-detail-close').click();
    await expect(page.locator('#workflow-research-detail')).toBeHidden();
    await page.locator('#workflow-research-operational').check();
    await expect(page.locator('.research-rq-card')).toHaveCount(1);
    await page.locator('#workflow-research-operational').uncheck();
    await selectRoute(page, 'old', 'wesen-symbiosis');
    await expect(page.locator('#wesen-neural-symbiosis')).toBeVisible();
    await expect(page.locator('#wesen-msba-pathways')).toContainText('Audio');
    await selectRoute(page, 'old', 'wesen-profile');
    await expect(page.locator('#wesen-profile-identity')).toBeVisible();
    await expect(page.locator('#wesen-profile-identity')).toContainText('Profile & Identität');
    await selectLabStage(page, 'question');
    await selectResearchView(page, 'files');
    await page.locator('#fm-popup-toggle').uncheck();
    await page.locator('.fm-source-btn[data-source="docs"]').click();
    const preview = page.locator('.fm-tree-file').filter({ has: page.locator('.fm-file-label', { hasText: 'preview.md' }) });
    const csv = page.locator('.fm-tree-file').filter({ has: page.locator('.fm-file-label', { hasText: 'data.csv' }) });
    await expect(preview).toBeVisible();
    await expect(csv).toBeVisible();
    await page.locator('.fm-filter-chip').filter({ hasText: /^Markdown$/ }).click();
    await expect(preview).toBeVisible();
    await expect(csv).toBeHidden();
    await page.locator('.fm-filter-chip').filter({ hasText: /^All$/ }).click();
    await expect(csv).toBeVisible();
    await preview.locator('.fm-file-label').click();
    const viewer = page.locator('#fm-viewer');
    await expect(viewer).toHaveAttribute('data-render-state', 'ready');
    await expect(viewer.locator('.file-renderer-body h2')).toHaveText('Gemeinsamer Renderer');
    await expect(viewer.locator('.file-renderer-body script')).toHaveCount(0);
    expect(await page.evaluate(() => window.unsafeExecuted)).toBeUndefined();
    await viewer.getByRole('button', { name: 'Im Chat anzeigen', exact: true }).click();
    const card = page.locator('#research-chat-log .chat-file-card .file-renderer');
    await expect(card).toHaveAttribute('data-render-state', 'ready');
    await expect(card.locator('.file-renderer-body')).toContainText(/Dateiinhalt|File content/);
    await expect(card.getByRole('button', { name: 'Bearbeiten', exact: true })).toHaveCount(0);
    await card.getByRole('button', { name: 'Messdaten', exact: true }).click();
    await expect(page.locator('[data-file-key="docs/sample.json"] .file-renderer')).toHaveAttribute('data-file-kind', 'json');
    await page.screenshot({ path: `test-results/fullstack-${port}.png`, fullPage: true });
    expect(errors).toEqual([]);
  });
}

test('real file service: edits persist, stale writers fail, originals stream', async ({ page }) => {
  await page.goto('http://127.0.0.1:4174/');
  const reference = 'notes/browser-edit.md';
  const url = `/api/files/document/${encodeURIComponent(reference)}?source=docs`;
  const created = await page.request.put(url, { data: { action: 'create', content: '# Original\n' } });
  expect(created.ok()).toBeTruthy();
  const data = await created.json();
  const edited = await page.request.put(url, { data: { action: 'write', content: '# Updated\n', expected_sha256: data.file.sha256 } });
  expect(edited.ok()).toBeTruthy();
  const stale = await page.request.put(url, { data: { action: 'write', content: 'stale', expected_sha256: data.file.sha256 } });
  expect(stale.status()).toBe(409);
  const raw = await page.request.get(`/api/files/raw/${encodeURIComponent(reference)}?source=docs`, { headers: { Range: 'bytes=0-8' } });
  expect(raw.status()).toBe(206);
  expect(await raw.text()).toBe('# Updated');
  const protectedWrite = await page.request.put('/api/files/document/registry%2Fquestions.yaml?source=research', { data: { action: 'write', content: '[]', expected_sha256: 'invalid' } });
  expect(protectedWrite.status()).toBe(403);
  const unknown = await page.request.get('/api/files/preview/unknown.b5d?source=docs');
  expect((await unknown.json()).kind).toBe('binary');
});

test('real registered batch: runner, manifest, DATA, report and central rendering', async ({ page }) => {
  await page.goto('http://127.0.0.1:4174/');
  const id = `EXP-BROWSER-${Date.now()}`;
  const response = await page.request.post('/api/experiment/workflow/batch', { data: {
    batch_id: id, protocols: ['temporal_order_spiking_v1'],
    protocol_options: { temporal_order_spiking_v1: { seeds: '101-120', ticks: 32 } },
  }, timeout: 30_000 });
  expect(response.ok()).toBeTruthy();
  const result = await response.json();
  expect(result.failed, JSON.stringify(result)).toBe(0);
  expect(result.completed).toBe(1);
  const path = `experiments/${id}-01/manifest.json`;
  const manifestResponse = await page.request.get(`/api/files/preview/${encodeURIComponent(path)}?source=research`);
  expect(manifestResponse.ok()).toBeTruthy();
  const descriptor = await manifestResponse.json();
  const manifest = JSON.parse(descriptor.content);
  expect(manifest.experiment_status).toBe('completed');
  expect(manifest.epistemic_layers.evid).toContain('not_created');
  expect(descriptor.read_only).toBe(true);
  await selectLabStage(page, 'question');
  await page.evaluate(async (filePath) => {
    const module = await import('/file-renderer.js');
    const host = document.createElement('section'); host.id = 'fullstack-artifact'; document.querySelector('#tab-research').prepend(host);
    await module.renderFile(host, { source: 'research', path: filePath });
  }, path);
  await expect(page.locator('#fullstack-artifact')).toHaveAttribute('data-render-state', 'ready');
  await expect(page.locator('#fullstack-artifact')).toContainText('temporal_order_spiking_v1');
});

test('real inventory changes reach the Wesen pipeline view without authorizing devices', async ({ page }) => {
  await page.goto('http://127.0.0.1:4174/');
  await selectRoute(page, 'old', 'wesen-symbiosis');
  await page.request.post('/__test__/inventory', { data: { available: true } });
  const camera = page.locator('#wesen-symbiosis-pipelines .wesen-symbiosis-item').filter({ hasText: 'Camera' });
  const robot = page.locator('#wesen-symbiosis-pipelines .wesen-symbiosis-item').filter({ hasText: 'Robotics' });
  await expect(camera).toHaveClass(/reachable/);
  await expect(camera).toContainText('endpoint reachable');
  await expect(robot).toContainText('endpoint reachable');
  await expect.poll(async () => {
    const response = await page.request.get('/api/embodiment/connections');
    const data = await response.json();
    return data.connections.find(item => item.connection_id === 'sensor.camera.browser')?.available;
  }).toBe(true);
  await page.request.post('/__test__/inventory', { data: { available: false } });
  await expect.poll(async () => {
    const response = await page.request.get('/api/embodiment/connections');
    const data = await response.json();
    const item = data.connections.find(item => item.connection_id === 'sensor.camera.browser');
    return item && !item.available && !item.authorized && !item.active;
  }).toBe(true);
  await expect(camera).toContainText('endpoint unavailable');
  await expect(robot).toContainText('endpoint unavailable');
  await expect(page.locator('#wesen-neural-symbiosis')).toContainText('EXPERIMENT CONTROL');
  await expect(page.locator('#wesen-neural-symbiosis')).toContainText('Productive Gateway: LOCKED');
});

test('canonical file viewer: split editor live preview and stale-write conflict diff', async ({ page }) => {
  await page.goto('http://127.0.0.1:4174/');
  const reference = `notes/split-editor-${Date.now()}.md`;
  const url = `/api/files/document/${encodeURIComponent(reference)}?source=docs`;
  const created = await page.request.put(url, { data: { action: 'create', content: '# Initial\n\nBody\n' } });
  expect(created.ok()).toBeTruthy();
  const createdData = await created.json();

  await selectRoute(page, 'files', 'browse');
  await page.locator('#fm-popup-toggle').uncheck();
  // Exercise the actual shared viewer and its layout, not an orphan body child
  // placed underneath fixed chrome by the test itself.
  await page.evaluate(filePath => window.openBrain5DFile('docs', filePath), reference);
  const host = page.locator('#fm-viewer');
  await expect(host).toHaveAttribute('data-render-state', 'ready');
  await host.getByRole('button', { name: 'Bearbeiten', exact: true }).click();
  await expect(host.locator('.file-renderer-editor-split')).toBeVisible();
  const editor = host.locator('.file-renderer-editor');
  await editor.fill('# Live Preview\n\nUpdated in browser');
  await expect(host.locator('.file-renderer-editor-preview')).toContainText('Live Preview');
  await expect(host.locator('.file-renderer-editor-preview')).toContainText('Updated in browser');

  const remote = await page.request.put(url, {
    data: { action: 'write', content: '# Remote Version\n', expected_sha256: createdData.file.sha256 },
  });
  expect(remote.ok()).toBeTruthy();
  await host.getByRole('button', { name: 'Speichern', exact: true }).click();
  await expect(host.locator('.file-renderer-body')).toContainText('Speicherkonflikt');
  await expect(host.locator('.file-renderer-body')).toContainText('Remote Version');
  await expect(host.locator('.file-renderer-body')).toContainText('Live Preview');
});

test('central research review inbox exposes human review actions', async ({ page }) => {
  await page.goto('http://127.0.0.1:4174/');
  await selectRoute(page, 'review', 'inbox');
  await expect(page.locator('#review-inbox-list')).toBeVisible();
  const inboxResponse = await page.request.get('/api/research/reviews');
  expect(inboxResponse.ok()).toBeTruthy();
  const inbox = await inboxResponse.json();
  expect(Array.isArray(inbox.items)).toBeTruthy();
  await expect(page.locator('#review-open-count')).toHaveText(String(inbox.open ?? 0));
  if (inbox.items.length) {
    await expect(page.locator('#review-inbox-list [data-review-reviewer]').first()).toBeVisible();
    await expect(page.locator('#review-inbox-list [data-review-comments]').first()).toBeVisible();
    await expect(page.locator('#review-inbox-list [data-review-decision="accepted_as_interpretation"]').first()).toBeVisible();
    await expect(page.locator('#review-inbox-list [data-review-decision="rejected"]').first()).toBeVisible();
  }
});

test('external review: subtab, public readiness and central viewer without private responses', async ({ page }) => {
  await page.goto('http://127.0.0.1:4174/');
  await selectLabStage(page, 'question');
  await selectResearchView(page, 'external');
  const panel = page.locator('#external-review-status');
  await expect(panel).toBeVisible();
  await expect(panel).toContainText(/135 (Questions|Fragen)/);
  await expect(panel).toContainText('Beurteilung ausstehend');
  const metadata = await (await page.request.get('/api/research/external-review')).json();
  expect(metadata.response_count).toBeNull();
  expect(metadata.scientific_evidence).toBe(false);
  await panel.locator('#external-review-method').click();
  await expect(page.locator('#fm-viewer-dialog')).toBeVisible();
  await expect(page.locator('#fm-dialog-viewer')).toHaveAttribute('data-render-state', 'ready');
  await expect(page.locator('#fm-dialog-viewer')).toContainText('Abschlusskriterien');
});

test('external review: stale success is cleared after failed status fetch', async ({ page }) => {
  await page.goto('http://127.0.0.1:4174/');
  await selectLabStage(page, 'question');
  await selectResearchView(page, 'external');
  await expect(page.locator('#external-review-summary')).toContainText(/135 (Questions|Fragen)/);
  await page.route('**/api/research/external-review', route => route.fulfill({ status: 503, body: '{}' }));
  await page.locator('#external-review-refresh').click();
  await expect(page.locator('#external-review-summary')).toContainText('Nicht verf\u00fcgbar');
  await expect(page.locator('#external-review-stages dt')).toHaveCount(0);
});