Skip to content

Commit e0cc8a9

Browse files
sametbasbugclaude
andcommitted
Only show the backup failures that are still open
Listing every failure ever recorded meant a run that broke three weeks ago and has succeeded every day since stayed permanently red. A warning that is always on is a warning nobody reads, and the question the card answers is not "has this ever failed" but "is it broken now". A failure older than the last successful run is closed. Those are a count in the status line; only failures newer than the last success get a row of their own, with the error code. Both states are covered: the fixture's old failure must not produce a row, and a second fixture whose latest run failed must turn the card red. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 4869c34 commit e0cc8a9

2 files changed

Lines changed: 79 additions & 19 deletions

File tree

‎scripts/orbit-browser-tests.mjs‎

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,19 @@ if (errors.length === 0) {
252252
backupKind: 'daily', status: 'succeeded',
253253
startedAt: Date.UTC(2026, 7, 14 - index, 6, 17), errorCode: null,
254254
}));
255+
/* Eski, KAPANMIŞ bir başarısızlık: ardından yedek defalarca
256+
* çalışmış. Panelde satır olarak durmamalı. */
255257
runs.splice(4, 0, {
256258
backupKind: 'weekly', status: 'failed',
257259
startedAt: Date.UTC(2026, 7, 9, 6, 17), errorCode: 'r2_upload_timeout',
258260
});
261+
/* İkinci senaryo: en son çalışma başarısız, yani iş açık. */
262+
if (request.headers.cookie?.includes('orbit-backup-broken=1')) {
263+
runs.unshift({
264+
backupKind: 'daily', status: 'failed',
265+
startedAt: Date.UTC(2026, 7, 15, 6, 17), errorCode: 'r2_upload_timeout',
266+
});
267+
}
259268
platformJson({ backups: runs });
260269
return;
261270
}
@@ -1001,20 +1010,19 @@ if (errors.length === 0) {
10011010
platform.announcementMeta === 'Kritik · Tüm ajanlar · Yayında',
10021011
`Platform: duyuru satırı ham enum yazıyor (${platform.announcementMeta}).`,
10031012
);
1004-
/* Yedek kartı: 34 satır yerine bir durum ve yalnız başarısız olan. */
1005-
check(platform.backupRows === 1, `Platform: yedek kartı başarılı çalışmaları da listeliyor (${platform.backupRows}).`);
1006-
check(platform.backupFailing === true, 'Platform: başarısız yedek varken durum satırı uyarı rengine geçmiyor.');
1013+
/* Yedek kartı, KAPANMIŞ başarısızlık hâli. Fikstürdeki başarısızlık
1014+
* eski ve ardından yedek defalarca çalışmış: kart yeşil kalmalı ve
1015+
* o satırı göstermemeli. Sürekli duran bir uyarı okunmayan bir
1016+
* uyarıdır — soru "hiç hata oldu mu" değil, "şu an bozuk mu". */
1017+
check(platform.backupRows === 0, `Platform: kapanmış başarısızlık hâlâ satır olarak duruyor (${platform.backupRows}).`);
1018+
check(platform.backupFailing === false, 'Platform: kapanmış başarısızlık için kart kırmızı kalıyor.');
10071019
check(
1008-
platform.backupSummary?.includes('1 yedek çalışması başarısız'),
1009-
`Platform: yedek durumu başarısızlığı söylemiyor (${platform.backupSummary}).`,
1020+
platform.backupSummary?.includes('Yedekler çalışıyor'),
1021+
`Platform: yedek durumu sağlam hâli söylemiyor (${platform.backupSummary}).`,
10101022
);
10111023
check(
1012-
platform.backupSummary?.includes('34 çalışmanın 33'),
1013-
`Platform: yedek durumu toplamı yanlış (${platform.backupSummary}).`,
1014-
);
1015-
check(
1016-
platform.backupRowText?.includes('Haftalık · Başarısız') && platform.backupRowText?.includes('r2_upload_timeout'),
1017-
`Platform: başarısız yedek satırı türü ya da hata kodunu yazmıyor (${platform.backupRowText}).`,
1024+
platform.backupSummary?.includes("1'i geçmişte başarısız") && platform.backupSummary?.includes('kapandı'),
1025+
`Platform: kapanmış başarısızlık sayı olarak da anılmıyor (${platform.backupSummary}).`,
10181026
);
10191027
/* Boş kutu: kart bir dönem 990px yüksekliğindeydi ve içinde 80px
10201028
* içerik vardı, çünkü yanındaki uzun kartla eşit boya çekiliyordu. */
@@ -1042,6 +1050,42 @@ if (errors.length === 0) {
10421050
await context.close();
10431051
}
10441052

1053+
/* Yedek kartı, AÇIK başarısızlık hâli: son çalışma düşmüş. Burada
1054+
* kart kırmızıya dönmeli ve hata kodunu yazmalı — bu, birinin
1055+
* gerçekten bakması gereken tek hâl. */
1056+
{
1057+
const context = await browser.newContext({ viewport: { width: 1440, height: 900 }, colorScheme: 'light' });
1058+
await context.addCookies([
1059+
{ name: 'orbit-platform-test', value: '1', url: baseUrl },
1060+
{ name: 'orbit-backup-broken', value: '1', url: baseUrl },
1061+
]);
1062+
const page = await context.newPage();
1063+
const pageErrors = [];
1064+
page.on('pageerror', (error) => pageErrors.push(error.message));
1065+
await page.goto(`${baseUrl}/dashboard/platform`, { waitUntil: 'load' });
1066+
await page.waitForSelector('.backup-summary');
1067+
const broken = await page.evaluate(() => ({
1068+
failing: document.querySelector('.backup-summary')?.classList.contains('is-failing'),
1069+
summary: document.querySelector('.backup-summary')?.textContent?.replace(/\s+/gu, ' ').trim(),
1070+
rows: document.querySelectorAll('#backups .dashboard-item').length,
1071+
rowText: document.querySelector('#backups .dashboard-item')?.textContent?.replace(/\s+/gu, ' ').trim(),
1072+
}));
1073+
check(broken.failing === true, 'Platform: açık başarısızlıkta durum satırı uyarı rengine geçmiyor.');
1074+
check(
1075+
broken.summary?.includes('Son başarılı yedekten beri 1 çalışma başarısız'),
1076+
`Platform: açık başarısızlık söylenmiyor (${broken.summary}).`,
1077+
);
1078+
/* Yalnız çözülmemiş olan listeleniyor: aynı fikstürde eski ve
1079+
* kapanmış bir başarısızlık daha var, o satır olarak çıkmamalı. */
1080+
check(broken.rows === 1, `Platform: kapanmış başarısızlık da listeleniyor (${broken.rows}).`);
1081+
check(
1082+
broken.rowText?.includes('Günlük · Başarısız') && broken.rowText?.includes('r2_upload_timeout'),
1083+
`Platform: açık başarısızlık satırı türü ya da hata kodunu yazmıyor (${broken.rowText}).`,
1084+
);
1085+
check(pageErrors.length === 0, `Platform açık yedek hatası: sayfa hatası: ${pageErrors.join(' | ')}`);
1086+
await context.close();
1087+
}
1088+
10451089
/* Yetkisi olmayan hesap. Sayfa bir kapı değil — uçlar sunucuda
10461090
* denetleniyor — ama çalışmayan düğmelerle dolu bir ekran da açmamalı. */
10471091
{

‎src/scripts/dashboard-platform.js‎

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,27 +162,43 @@ async function loadBackups() {
162162
host.innerHTML = '<div class="dashboard-item"><strong>Henüz yedek çalışması yok</strong></div>';
163163
return;
164164
}
165-
const failed = rows.filter((run) => run.status === 'failed');
166165
const running = rows.filter((run) => run.status === 'running');
167166
const latest = rows[0];
168167
const lastSucceeded = rows.find((run) => run.status === 'succeeded');
169168

169+
/* ÇÖZÜLMEMİŞ başarısızlık: son başarılı çalışmadan SONRA olan.
170+
*
171+
* Bir başarısızlığın ardından yedek yeniden çalıştıysa o iş kapanmıştır.
172+
* Üç hafta önce takılmış bir koşuyu panelde tutmak, kimsenin bir daha
173+
* yapamayacağı bir şeyi kalıcı olarak kırmızı göstermek olur — ve
174+
* sürekli duran bir uyarı, bir süre sonra hiç okunmayan bir uyarıdır.
175+
* Asıl soru "hiç hata oldu mu" değil, "şu an bozuk mu". */
176+
const unresolved = lastSucceeded
177+
? rows.filter((run) => run.status === 'failed' && run.startedAt > lastSucceeded.startedAt)
178+
: rows.filter((run) => run.status === 'failed');
179+
const failedTotal = rows.filter((run) => run.status === 'failed').length;
180+
170181
const summary = document.createElement('div');
171-
summary.className = `backup-summary${failed.length > 0 ? ' is-failing' : ''}`;
182+
summary.className = `backup-summary${unresolved.length > 0 ? ' is-failing' : ''}`;
172183
summary.innerHTML = `
173-
<strong>${failed.length > 0
174-
? `${failed.length} yedek çalışması başarısız`
184+
<strong>${unresolved.length > 0
185+
? `Son başarılı yedekten beri ${unresolved.length} çalışma başarısız`
175186
: 'Yedekler çalışıyor'}</strong>
176187
<div class="meta">Son çalışma: ${escapeHtml(backupKindLabel(latest.backupKind))} · ${escapeHtml(backupStatusLabel(latest.status))} · <span title="${escapeHtml(absoluteTime(latest.startedAt))}">${escapeHtml(relativeTime(latest.startedAt))}</span></div>
177188
${lastSucceeded && lastSucceeded !== latest
178189
? `<div class="meta">Son başarılı: <span title="${escapeHtml(absoluteTime(lastSucceeded.startedAt))}">${escapeHtml(relativeTime(lastSucceeded.startedAt))}</span></div>`
179190
: ''}
180-
<div class="meta">Kayıtlı ${rows.length} çalışmanın ${rows.length - failed.length}'i başarılı${running.length > 0 ? `, ${running.length}'i sürüyor` : ''}.</div>`;
191+
${running.length > 0 ? `<div class="meta">${running.length} çalışma sürüyor.</div>` : ''}
192+
${/* Kapanmış başarısızlıklar sayı olarak kalıyor: kayıt tutulmuş
193+
olmasının bir değeri var, ama satır satır göstermenin yok. */
194+
unresolved.length === 0 && failedTotal > 0
195+
? `<div class="meta">Kayıtlı ${rows.length} çalışmanın ${failedTotal}'i geçmişte başarısız olmuş, hepsi sonraki koşularla kapandı.</div>`
196+
: ''}`;
181197
host.append(summary);
182198

183-
/* Başarısızlar tek tek yazılıyor: hata kodu olmadan "başarısız" demek,
184-
bakan kişiye ne yapacağını söylemiyor. */
185-
for (const run of failed) {
199+
/* Yalnız çözülmemişler tek tek yazılıyor: hata kodu olmadan "başarısız"
200+
demek, bakan kişiye ne yapacağını söylemiyor. */
201+
for (const run of unresolved) {
186202
const item = document.createElement('div');
187203
item.className = 'dashboard-item is-error';
188204
item.innerHTML = `<strong>${escapeHtml(backupKindLabel(run.backupKind))} · ${escapeHtml(backupStatusLabel(run.status))}</strong><div class="meta">${escapeHtml(absoluteTime(run.startedAt))}${run.errorCode ? ` · ${escapeHtml(run.errorCode)}` : ''}</div>`;

0 commit comments

Comments
 (0)