Test Specs IV2 - MZC-CSC/mc-web-console GitHub Wiki
μμ±: 2026-10-14 Β· κΈ°μ€: v07 μ€μΉ ν ν΅ν© κ²μ¦(IV) Β· μλ³Έ: μ¬μ λ³ κ³νμ(Excel) β Test Scenarios Β· IV νλΈ Β· κΈ°λ₯ TC
Playwright(TypeScript) spec μμ€μ
λλ€. μλ리μ€λ§λ€ ## <μλ리μ€λ²νΈ> μΉμ
μ΄ μκ³ μ΅μ»€λ #<μλ리μ€λ²νΈ μλ¬Έμ> μ
λλ€. κ³΅μ© ν¬νΌλ Test-Specs-Common. λΉλ°λ²νΈ λ±μ λ§μ€νΉνμ΅λλ€.
| μλλ¦¬μ€ | νμΌ | μ€ | ν΄μ |
|---|---|---|---|
| IV2_S01 | tests/iv2/IV2_S01.spec.ts |
15 | c4db847c |
| IV2_S02 | tests/iv2/IV2_S02.spec.ts |
15 | 2591f598 |
| IV2_S03 | tests/iv2/IV2_S03.spec.ts |
15 | c6cedb0c |
| IV2_S04 | tests/iv2/IV2_S04.spec.ts |
15 | deec07d8 |
| IV2_S05 | tests/iv2/IV2_S05.spec.ts |
53 | a954ea92 |
| IV2_S06 | tests/iv2/IV2_S06.spec.ts |
88 | 42078aef |
| IV2_S07 | tests/iv2/IV2_S07.spec.ts |
31 | 60b0a2d9 |
| IV2_S08 | tests/iv2/IV2_S08.spec.ts |
41 | 425616c0 |
tests/iv2/IV2_S01.spec.ts Β· 15μ€ Β· ν΄μ c4db847c Β· μλλ¦¬μ€ λ³΄κΈ°
import { test } from '../../lib/fixtures';
import { activeCsps, tid } from '../../lib/data';
import { createListFilter, resourceBlocker } from '../../lib/resources';
// IV2_S01 : vNet μμ± β λͺ©λ‘ νμ β CSP νν° [μ μ, CSPλ³]
// Cloud Resources > Networks (Create VPC) β λͺ©λ‘ β Provider
// μ¬μ€ν κ°λ₯: κ°μ μ΄λ¦μ΄ μ΄λ―Έ μμΌλ©΄ μμ±μ 건λλ°κ³ λͺ©λ‘/νν°λ§ κ²μ¦νλ€. μμ
μ CSP νκ³Ό 1:1 (TC_VARIANT λ‘ CSP μ ν).
for (const c of activeCsps()) {
test(`${tid('IV2_S01', c)} vNet μμ± β λͺ©λ‘ νμ β CSP νν°`, async ({ page }) => {
const why = resourceBlocker(c, 'vnet');
test.skip(!!why, why);
await createListFilter(page, c, 'vnet');
});
}tests/iv2/IV2_S02.spec.ts Β· 15μ€ Β· ν΄μ 2591f598 Β· μλλ¦¬μ€ λ³΄κΈ°
import { test } from '../../lib/fixtures';
import { activeCsps, tid } from '../../lib/data';
import { createListFilter, resourceBlocker } from '../../lib/resources';
// IV2_S02 : Security Group μμ± β λͺ©λ‘ νμ β CSP νν° [μ μ, CSPλ³]
// Cloud Resources > Security Groups (Create SG) β λͺ©λ‘ β Provider. μ ν: κ°μ CSP μ vNet(IV2_S01)
// μ¬μ€ν κ°λ₯: κ°μ μ΄λ¦μ΄ μ΄λ―Έ μμΌλ©΄ μμ±μ 건λλ°κ³ λͺ©λ‘/νν°λ§ κ²μ¦νλ€.
for (const c of activeCsps()) {
test(`${tid('IV2_S02', c)} Security Group μμ± β λͺ©λ‘ νμ β CSP νν°`, async ({ page }) => {
const why = resourceBlocker(c, 'sg');
test.skip(!!why, why);
await createListFilter(page, c, 'sg');
});
}tests/iv2/IV2_S03.spec.ts Β· 15μ€ Β· ν΄μ c6cedb0c Β· μλλ¦¬μ€ λ³΄κΈ°
import { test } from '../../lib/fixtures';
import { activeCsps, tid } from '../../lib/data';
import { createListFilter, resourceBlocker } from '../../lib/resources';
// IV2_S03 : SSH Key μμ± β λͺ©λ‘ νμ β CSP νν° [μ μ, CSPλ³]
// Cloud Resources > SSH Keys (Create SSH Key) β λͺ©λ‘ β Provider
// μ¬μ€ν κ°λ₯: κ°μ μ΄λ¦μ΄ μ΄λ―Έ μμΌλ©΄ μμ±μ 건λλ°κ³ λͺ©λ‘/νν°λ§ κ²μ¦νλ€.
for (const c of activeCsps()) {
test(`${tid('IV2_S03', c)} SSH Key μμ± β λͺ©λ‘ νμ β CSP νν°`, async ({ page }) => {
const why = resourceBlocker(c, 'key');
test.skip(!!why, why);
await createListFilter(page, c, 'key');
});
}tests/iv2/IV2_S04.spec.ts Β· 15μ€ Β· ν΄μ deec07d8 Β· μλλ¦¬μ€ λ³΄κΈ°
import { test } from '../../lib/fixtures';
import { activeCsps, tid } from '../../lib/data';
import { createListFilter, resourceBlocker } from '../../lib/resources';
// IV2_S04 : Data Disk μμ± β λͺ©λ‘ νμ β CSP νν° [μ μ, CSPλ³]
// Cloud Resources > Disks (Create Data Disk) β λͺ©λ‘ β Provider
// μ¬μ€ν κ°λ₯: κ°μ μ΄λ¦μ΄ μ΄λ―Έ μμΌλ©΄ μμ±μ 건λλ°κ³ λͺ©λ‘/νν°λ§ κ²μ¦νλ€.
for (const c of activeCsps()) {
test(`${tid('IV2_S04', c)} Data Disk μμ± β λͺ©λ‘ νμ β CSP νν°`, async ({ page }) => {
const why = resourceBlocker(c, 'disk');
test.skip(!!why, why);
await createListFilter(page, c, 'disk');
});
}tests/iv2/IV2_S05.spec.ts Β· 53μ€ Β· ν΄μ a954ea92 Β· μλλ¦¬μ€ λ³΄κΈ°
import { test, expect } from '../../lib/fixtures';
import { loginAsAdmin, menuSnapshot, selectWorkspaceProject } from '../../lib/ui';
import { WORKSPACE, DEFAULT_PROJECT } from '../../lib/data';
import { filterByProvider } from '../../lib/resources';
// IV2_S05 : Cloud Resources 8μ’
λͺ©λ‘ ν΅ν© μ‘°ν (λ©ν°ν΄λΌμ°λ) -> CSP νν°
// μ€μ λ©λ΄λͺ
(2026-10-01 νμΈ): Specs, Images, Networks, Security Groups, MyImages, SSH Keys, Disks, NLB (+ Resource Sync)
// λ©λ΄ ν΄λ¦μ λλ‘λ€μ΄ μ κ°/μ ν μ΄κΈ°νλ‘ timeout μ΄ λ¬μΌλ―λ‘(2026-10-02) λ©λ΄ href λ₯Ό μ½μ΄ μ§μ μ΄λνκ³ , μ΄λ ν ws01/default λ₯Ό λ€μ μ ννλ€.
const CLOUD_RESOURCES = ['Networks', 'Security Groups', 'SSH Keys', 'Disks', 'Specs', 'Images', 'MyImages', 'NLB'];
// IV2_S01~S04 λ‘ μ¬λ¬ CSP μμμ΄ λ§λ€μ΄μ§λ λͺ©λ‘: 2κ° μ΄μ CSP κ° ν¨κ» νμ + Provider νν° κ²μ¦ λμ
const MULTI = ['Networks', 'Security Groups', 'SSH Keys', 'Disks'];
const norm = (s: string) => s.toLowerCase().replace(/\s+/g, '');
test('IV2_S05 8μ’
λͺ©λ‘ ν΅ν© μ‘°ν (λ©ν°ν΄λΌμ°λ) β CSP νν°', async ({ page }) => {
test.setTimeout(20 * 60_000);
const errors: string[] = [];
page.on('pageerror', (e) => errors.push(e.message));
await loginAsAdmin(page); // admin, ws01 / default
const links = (await menuSnapshot(page)).map((l) => l.split(' -> ')).filter(([, href]) => href?.includes('/cloudresources/') || /serverspecs|serverimages|nlbs/.test(href ?? ''));
test.info().annotations.push({ type: 'menu', description: links.map(([t, h]) => `${t}=${h}`).join(', ') });
for (const name of CLOUD_RESOURCES) {
const hit = links.find(([t]) => norm(t) === norm(name));
expect(hit, `Cloud Resources λ©λ΄μ ${name} μ‘΄μ¬`).toBeTruthy();
await page.goto(hit![1]);
await selectWorkspaceProject(page, WORKSPACE, DEFAULT_PROJECT, true);
await page.getByText(/Showing \d+-\d+ of \d+ rows|No registered|No .* (found|registered)|Search with connection/i).first().waitFor({ timeout: 30_000 }).catch(() => {});
await page.waitForTimeout(1500);
await expect(page.locator('.tabulator, table, [role="grid"]').first(), `${name} λͺ©λ‘ νμ`).toBeVisible();
const status = await page.getByText(/Showing \d+-\d+ of \d+ rows/).first().innerText().catch(() => '(ν μμ/μλ΄ λ¬Έκ΅¬)');
// νλ³ [Name, Provider] (ν€λ μ λͺ©μΌλ‘ μ΄ μ°ΎκΈ°)
const rows = await page.locator('.tabulator').first().evaluate((t) => {
const cols = [...t.querySelectorAll('.tabulator-header .tabulator-col')];
const f = (title: string) => cols.find((c) => (c.textContent ?? '').trim() === title)?.getAttribute('tabulator-field');
const [fn, fp] = [f('Name'), f('Provider')];
if (!fp) return [] as string[][];
const cell = (r: Element, k?: string | null) => (k ? r.querySelector(`.tabulator-cell[tabulator-field="${k}"]`)?.textContent ?? '' : '').trim();
return [...t.querySelectorAll('.tabulator-row')].map((r) => [cell(r, fn), cell(r, fp).toLowerCase()]);
}).catch(() => [] as string[][]);
const uniq = [...new Set(rows.map((r) => r[1]).filter(Boolean))];
test.info().annotations.push({ type: `list:${name}`, description: `${status} / Provider: ${uniq.join(',') || '-'}` });
if (MULTI.includes(name)) {
expect(uniq.length, `${name}: 2κ° μ΄μ CSP μμμ΄ ν¨κ» νμ`).toBeGreaterThanOrEqual(2);
// aws(μμΌλ©΄ 첫 CSP)λ‘ νν° β κ·Έ CSP μμλ§ λ¨λμ§ κ²μ¦
const prov = uniq.includes('aws') ? 'aws' : uniq[0];
await filterByProvider(page, prov, rows.find((r) => r[1] === prov)![0]);
}
}
expect(errors, 'JS μμΈ μμ').toEqual([]);
});tests/iv2/IV2_S06.spec.ts Β· 88μ€ Β· ν΄μ 42078aef Β· μλλ¦¬μ€ λ³΄κΈ°
// IV2_S06: Resource Sync: NHN vNet λκΈ°ν β Networks λͺ©λ‘μ NHN vNet νμ [μ μ, NHN Cloud] (μ¬μ©μ μ§μ 2026-10-08)
// νλ©΄: Cloud Resources > Resource Sync (Sync Status Resources ν) β Cloud Resources > Networks
// μ μ°¨: 1.Resource Sync μ§μ
2.Search Conditions: Provider=Partial β NHN 체ν¬, Connection=NHN μ²΄ν¬ β nhn-kr1, nhn-kr2 체ν¬, Resource Type=Partial β vNet μ²΄ν¬ β Query
// 3.CSP Resource Sync Status μμ Connection nhn-kr1, nhn-kr2 μ ν 4.Run Sync: Namespace=default, Target Provider=Partial β NHN 체ν¬, Resource Type=Partial β vNet μ²΄ν¬ β Run Sync
// 5.Cloud Resources > Networks μμ NHN vNet μ΄ λͺ©λ‘μ νμλλμ§ νμΈ(μ¬μ©μ μ§μ: νμΈ λ°©λ²)
// κΈ°λκ²°κ³Ό: Run Sync μν ν Networks λͺ©λ‘μ Provider nhn μ vNet μ΄ μλ€
// νλ©΄ μμ(2026-10-08 νλ‘λΈ): μ‘°ν 쑰건 radio name=query-provider-radio / query-restype-radio, 체ν¬λ°μ€ value=nhn, nhn-kr1, nhn-kr2, vNet, Run Sync μμ #sync-ns, radio name=provider-radio / restype-radio
import { test, expect } from '../../lib/fixtures';
import { activeCsps, tid } from '../../lib/data';
import { loginAsAdmin, selectWorkspaceProject, toasts } from '../../lib/ui';
const SYNC_URL = '/webconsole/settings/environment/cloudresources/resourcesync';
const NETWORKS_URL = '/webconsole/settings/environment/cloudresources/networks';
const CONNS = ['nhn-kr1', 'nhn-kr2'];
for (const c of activeCsps(['NHN Cloud'])) {
test(`${tid('IV2_S06', c)} Resource Sync: NHN vNet λκΈ°ν β Networks λͺ©λ‘μ NHN vNet νμ`, async ({ page }) => {
test.setTimeout(15 * 60_000);
await loginAsAdmin(page);
await page.goto(SYNC_URL);
await selectWorkspaceProject(page, 'ws01', 'default', true);
await expect(page.locator('input[name="query-provider-radio"][value="partial"]')).toBeVisible({ timeout: 60_000 });
await page.waitForTimeout(3000);
// 2. Search Conditions
await page.locator('input[name="query-provider-radio"][value="partial"]').check();
await page.locator('input[value="nhn"]').first().check(); // Provider NHN
await expect(page.locator('input[value="nhn-kr1"]').first(), 'Provider NHN μ²΄ν¬ ν Connection λͺ©λ‘ νμ').toBeVisible({ timeout: 15_000 });
await page.locator('input[value="nhn"]').nth(1).check(); // Connection κ·Έλ£Ή NHN
for (const cn of CONNS) {
const box = page.locator(`input[value="${cn}"]`).first();
if (!(await box.isChecked())) await box.check();
await expect(box, `${cn} 체ν¬`).toBeChecked();
}
await page.locator('input[name="query-restype-radio"][value="partial"]').check();
await page.locator('input[value="vNet"]').first().check(); // μ‘°ν 쑰건μ vNet
await page.getByRole('button', { name: 'Query', exact: true }).click();
// 3. CSP Resource Sync Status: 'Fetching resource status...' κ° λλλ©΄(μ½ 15μ΄) μ°κ²°λ³ ν(Connection / vNet λ±λ‘μ/μ 체μ)μ΄ λμ¨λ€ β νμ λλ¬ μ°κ²° μ ν
await expect(page.getByText('Fetching resource status')).toBeHidden({ timeout: 180_000 });
await page.waitForTimeout(1500);
const bodyAfterQuery = (await page.locator('body').innerText()).replace(/\s+/g, ' ');
const si = bodyAfterQuery.indexOf('CSP Resource Sync Status');
test.info().annotations.push({ type: 'sync-status', description: bodyAfterQuery.slice(si, si + 400) });
for (const cn of CONNS) {
const row = page.getByRole('row').filter({ has: page.getByRole('gridcell', { name: cn, exact: true }) }).first();
await expect(row, `Sync Status μ ${cn} ν`).toBeVisible({ timeout: 30_000 });
await row.getByRole('checkbox').first().check(); // ν ν΄λ¦μ Connection Detail λ§ μ΄κ³ , Target Connection μ ν 체ν¬λ°μ€λ‘ μ νλλ€
}
const target = (await page.locator('body').innerText()).replace(/\s+/g, ' ');
test.info().annotations.push({ type: 'target-connection', description: target.slice(target.indexOf('Target Connection'), target.indexOf('Target Connection') + 160) });
// 4. Run Sync (Namespace default, Target Provider partial+NHN, Resource Type partial+vNet)
const run = page.locator('form, div').filter({ has: page.locator('#sync-ns') }).last();
await page.locator('#sync-ns').selectOption({ label: 'default' });
await page.locator('input[name="provider-radio"][value="partial"]').check();
await page.waitForTimeout(800);
// Run Sync μμμ NHN / vNet 체ν¬λ°μ€(μ‘°ν 쑰건μ κ°μ μ΄λ¦ 체ν¬λ°μ€μ ꡬλΆνλ €κ³ ν΄λΉ λΌλμ€ λ°λ‘ λ€μ 첫 체ν¬λ°μ€λ₯Ό μ΄λ€)
const runNhn = page.locator('input[name="provider-radio"][value="partial"]').locator('xpath=following::input[@value="nhn"][1]');
if (!(await runNhn.isChecked())) await runNhn.check(); // μ°κ²°μ κ³ λ₯΄λ©΄ NHN μ΄ μλ 체ν¬λμ΄ μμ
await page.locator('input[name="restype-radio"][value="partial"]').check();
await page.waitForTimeout(800);
await page.locator('input[name="restype-radio"][value="partial"]').locator('xpath=following::input[@value="vNet"][1]').check();
test.info().annotations.push({ type: 'run-sync-form', description: (await run.innerText().catch(() => '')).replace(/\s+/g, ' ').slice(0, 300) });
await page.getByRole('button', { name: 'Run Sync', exact: true }).click();
// κ²°κ³Ό: νμΈ νμ
/ν μ€νΈ/κ²°κ³Ό 문ꡬ μμ§(μ΅λ ~2λΆ)
const msgs = new Set<string>();
for (let i = 0; i < 24; i++) {
for (const m of await page.locator('.modal.show').all()) { const t = (await m.innerText().catch(() => '')).replace(/\s+/g, ' ').trim(); if (t) msgs.add(`modal: ${t.slice(0, 300)}`); }
for (const t of await toasts(page)) { const s = t.replace(/\s+/g, ' ').trim(); if (s) msgs.add(`toast: ${s.slice(0, 300)}`); }
const confirm = page.locator('.modal.show').getByRole('button', { name: /Confirm|OK|Yes|Run/i }).last();
if (await confirm.isVisible().catch(() => false)) await confirm.click().catch(() => {});
await page.waitForTimeout(5000);
}
test.info().annotations.push({ type: 'run-sync-messages', description: [...msgs].join(' || ').slice(0, 800) || '(λ©μμ§ μμ)' });
const after = (await page.locator('body').innerText()).replace(/\s+/g, ' ');
test.info().annotations.push({ type: 'sync-after', description: after.slice(after.indexOf('Run Sync'), after.indexOf('Run Sync') + 500) });
// 5. Networks λͺ©λ‘μ NHN vNet
await page.goto(NETWORKS_URL);
await selectWorkspaceProject(page, 'ws01', 'default', true);
await page.getByText(/Showing \d+-\d+ of \d+ rows|No registered/).first().waitFor({ timeout: 30_000 }).catch(() => {});
await page.waitForTimeout(2000);
const rows = (await page.locator('.tabulator-row').allInnerTexts()).map((s) => s.replace(/\s+/g, ' ')).filter((s) => /nhn/i.test(s));
test.info().annotations.push({ type: 'networks-nhn', description: rows.join(' || ') || '(NHN vNet μμ)' });
expect(rows.length, 'Networks λͺ©λ‘μ NHN vNet').toBeGreaterThan(0);
});
}tests/iv2/IV2_S07.spec.ts Β· 31μ€ Β· ν΄μ 60b0a2d9 Β· μλλ¦¬μ€ λ³΄κΈ°
// IV2_S07: Resource Sync: NCP vNetΒ·Security Group λκΈ°ν β Networks / Security Groups λͺ©λ‘μ NCP νμ [μ μ, NCP] (μ¬μ©μ μ§μ 2026-10-08)
// λ°°κ²½: NCP κ³μ μ VPC μ κ· μμ±μ΄ λ§ν(returnCode 1015009 'You can no longer create a product') IV2_S01/S02 λ₯Ό μ κ· μμ±μΌλ‘ ν μ μλ€ β κΈ°μ‘΄ NCP VPC/SG λ₯Ό Resource Sync λ‘ κ°μ Έμ μ¬μ©
// νλ©΄: Cloud Resources > Resource Sync β Cloud Resources > Networks, Security Groups
// μ μ°¨: 1.Resource Sync μ§μ
2.Search Conditions: Provider=Partial β NCP, Connection=NCP β ncp-kr, Resource Type=Partial β vNet, Security Group β Query
// 3.CSP Resource Sync Status μμ ncp-kr μ ν 4.Run Sync: Namespace=default, Target Provider=Partial β NCP, Resource Type=Partial β vNet, Security Group β Run Sync
// 5.Networks λͺ©λ‘μ NCP vNet, Security Groups λͺ©λ‘μ NCP SG νμ νμΈ
// κΈ°λκ²°κ³Ό: λκΈ°ν ν λ λͺ©λ‘μ Provider ncp μμμ΄ μλ€
// λκΈ°ν λμ μ°κ²°: SYNC_CONNS νκ²½λ³μ(μΌν, κΈ°λ³Έ ncp-kr β IV2 κ° μ°λ μ°κ²°)
import { test, expect } from '../../lib/fixtures';
import { activeCsps, tid } from '../../lib/data';
import { loginAsAdmin } from '../../lib/ui';
import { runResourceSync, listProviderRows } from '../../lib/sync';
const NETWORKS_URL = '/webconsole/settings/environment/cloudresources/networks';
const SG_URL = '/webconsole/settings/environment/cloudresources/securitygroups';
for (const c of activeCsps(['NCP'])) {
test(`${tid('IV2_S07', c)} Resource Sync: NCP vNetΒ·Security Group λκΈ°ν β λͺ©λ‘μ NCP νμ`, async ({ page }) => {
test.setTimeout(15 * 60_000);
const conns = (process.env.SYNC_CONNS || 'ncp-kr').split(',').map((s) => s.trim()).filter(Boolean);
await loginAsAdmin(page);
await runResourceSync(page, { provider: 'ncp', conns, types: ['vNet', 'securityGroup'] });
const nets = await listProviderRows(page, NETWORKS_URL, 'ncp');
test.info().annotations.push({ type: 'networks-ncp', description: nets.map((s) => s.slice(0, 90)).join(' || ') || '(NCP vNet μμ)' });
const sgs = await listProviderRows(page, SG_URL, 'ncp');
test.info().annotations.push({ type: 'securitygroups-ncp', description: `${sgs.length}건: ${sgs.slice(0, 5).map((s) => s.slice(0, 70)).join(' || ') || '(NCP SG μμ)'}` });
expect.soft(nets.length, 'Networks λͺ©λ‘μ NCP vNet').toBeGreaterThan(0);
expect.soft(sgs.length, 'Security Groups λͺ©λ‘μ NCP SG').toBeGreaterThan(0);
});
}tests/iv2/IV2_S08.spec.ts Β· 41μ€ Β· ν΄μ 425616c0 Β· μλλ¦¬μ€ λ³΄κΈ°
// IV2_S08: Resource Sync ν CSP Resource Sync Status λ±λ‘μ νμΈ (λͺ©λ‘κ³Ό μΌμΉ) [μ μ, NHN CloudΒ·NCP] (μ¬μ©μ μ§μ 2026-10-08)
// νλ©΄: Cloud Resources > Resource Sync (Search Conditions β Query β CSP Resource Sync Status μ μ°κ²°λ³ λ±λ‘μ/μ 체μ) β Cloud Resources > Networks, Security Groups
// μ μ°¨: 1.Resource Sync μ§μ
2.Provider/Connection/Resource Type 쑰건 β Query(Run Sync λ νμ§ μμ) 3.μ°κ²°λ³ λ±λ‘μ/μ 체μ μ½κΈ° 4.λͺ©λ‘(Networks / Security Groups)μμ κ°μ μ°κ²°(μ΄λ¦μ΄ '<μ°κ²°>-' λ‘ μμ)μ κ°μ μΈκΈ° 5.λΉκ΅
// κΈ°λκ²°κ³Ό: λκΈ°νν μ°κ²°μ λ±λ‘μ = μ 체μ μ΄κ³ , λͺ©λ‘ κ°μμ κ°λ€. λ€λ₯΄λ©΄ FAIL(κ°μ λΉκ³ μ κΈ°λ‘) β NHN nhn-kr2 λ λͺ©λ‘ 3κ°μΈλ° Sync Status 0/3 μΌλ‘ λ¨λ λΆμΌμΉκ° κ΄μ°°λ¨(2026-10-08)
// μ ν: NHN β IV2_S06, NCP β IV2_S07 (λκΈ°ν μλ£)
import { test, expect } from '../../lib/fixtures';
import { activeCsps, tid } from '../../lib/data';
import { loginAsAdmin } from '../../lib/ui';
import { queryResourceStatus, listProviderRows } from '../../lib/sync';
const NET = '/webconsole/settings/environment/cloudresources/networks';
const SG = '/webconsole/settings/environment/cloudresources/securitygroups';
const CFG: Record<string, { provider: string; conns: string[]; types: Array<{ value: 'vNet' | 'securityGroup'; label: string }> }> = {
NHNCloud: { provider: 'nhn', conns: ['nhn-kr1', 'nhn-kr2'], types: [{ value: 'vNet', label: 'vNet' }] },
NCP: { provider: 'ncp', conns: ['ncp-kr'], types: [{ value: 'vNet', label: 'vNet' }, { value: 'securityGroup', label: 'Security Group' }] },
};
for (const c of activeCsps(['NHN Cloud', 'NCP'])) {
test(`${tid('IV2_S08', c)} Resource Sync ν CSP Resource Sync Status λ±λ‘μ νμΈ (λͺ©λ‘κ³Ό μΌμΉ)`, async ({ page }) => {
test.setTimeout(10 * 60_000);
const cfg = CFG[c.code];
await loginAsAdmin(page);
const status = await queryResourceStatus(page, cfg);
test.info().annotations.push({ type: 'sync-status', description: JSON.stringify(status) });
const lists: Record<string, string[]> = {};
for (const t of cfg.types) lists[t.label] = await listProviderRows(page, t.label === 'vNet' ? NET : SG, cfg.provider);
const mismatches: string[] = [];
for (const cn of cfg.conns) {
for (const t of cfg.types) {
const m = (status[cn][t.label] ?? '').match(/^(\d+)\/(\d+)$/);
const listed = lists[t.label].filter((r) => r.startsWith(`${cn}-`)).length;
const reg = m ? Number(m[1]) : -1, total = m ? Number(m[2]) : -1;
test.info().annotations.push({ type: 'compare', description: `${cn} ${t.label}: Sync Status ${status[cn][t.label]} / λͺ©λ‘ ${listed}κ°` });
if (reg !== total) mismatches.push(`${cn} ${t.label}: λ±λ‘μ ${reg} β μ 체μ ${total}`);
if (reg !== listed) mismatches.push(`${cn} ${t.label}: Sync Status λ±λ‘μ ${reg} β λͺ©λ‘ ${listed}κ°`);
}
}
expect(mismatches, `Sync Status μ λͺ©λ‘ λΆμΌμΉ: ${mismatches.join(' | ')}`).toEqual([]);
});
}