# Instructions

- Following Playwright test failed.
- Explain why, be concise, respect Playwright best practices.
- Provide a snippet of code with the fix, if possible.

# Test info

- Name: tests/ui/district-sitemap-audit.spec.ts >> districts sitemap: every listed URL >> each URL is a well-formed 200 canonical page
- Location: tests/ui/district-sitemap-audit.spec.ts:43:7

# Error details

```
Error: 1 of 7272 district sitemap URLs are stale or broken:
/districts/ak-hd-022/: expected 200, got 503

expect(received).toEqual(expected) // deep equality

- Expected  - 1
+ Received  + 3

- Array []
+ Array [
+   "/districts/ak-hd-022/: expected 200, got 503",
+ ]
```

# Test source

```ts
  1  | import { test, expect } from '@playwright/test';
  2  | import { canonicalPath, toRelativePath } from './helpers';
  3  | 
  4  | /*
  5  |  * Exhaustive district sitemap audit (electinfo/sites#462).
  6  |  *
  7  |  * sitemap.spec.ts and routing.spec.ts sample a handful of sitemap URLs, so stale rows
  8  |  * for a few districts (the AS/GU/MP/VI `-00` aliases pointed at retired UUIDs and 404ed
  9  |  * while the sitemap still listed them) never surfaced. This audit requests EVERY URL the
  10 |  * districts sitemap lists, with no redirects, and requires a final 200 whose rel=canonical
  11 |  * is the sitemap URL itself. Failures are collected and reported together so one run shows
  12 |  * the whole stale set.
  13 |  *
  14 |  * No entity is required or excluded by name: whatever the published sitemap lists is
  15 |  * audited, so a territory the graph no longer carries must simply be absent from the
  16 |  * sitemap, while a listed one must serve. Only the URL shape is asserted broadly.
  17 |  */
  18 | 
  19 | const SITEMAP = '/districts/sitemap-districts.xml';
  20 | const STRICT = process.env.EXPECT_ALIASES === '1';
  21 | const CONCURRENCY = 8;
  22 | 
  23 | // /districts/<slug>/: lowercase alphanumeric hyphenated slug, which covers congressional
  24 | // (ak-00), state legislative (ny-sd-055, TIGER alphanumerics) and graph-UUID paths
  25 | const DISTRICT_PATH = /^\/districts\/[a-z0-9]+(-[a-z0-9]+)*\/$/;
  26 | 
  27 | const locs = (xml: string) => [...xml.matchAll(/<loc>([^<]+)<\/loc>/g)].map(m => m[1]);
  28 | 
  29 | /** Run `fn` over `items` with bounded concurrency. */
  30 | async function mapBounded<T, R>(items: T[], limit: number, fn: (item: T) => Promise<R>): Promise<R[]> {
  31 |   const out: R[] = new Array(items.length);
  32 |   let next = 0;
  33 |   await Promise.all(Array.from({ length: Math.min(limit, items.length) }, async () => {
  34 |     while (next < items.length) {
  35 |       const i = next++;
  36 |       out[i] = await fn(items[i]);
  37 |     }
  38 |   }));
  39 |   return out;
  40 | }
  41 | 
  42 | test.describe('districts sitemap: every listed URL', () => {
  43 |   test('each URL is a well-formed 200 canonical page', async ({ request }) => {
  44 |     test.setTimeout(600000);
  45 | 
  46 |     const res = await request.get(SITEMAP);
  47 |     if (res.status() === 404 && !STRICT) test.skip(true, `${SITEMAP} not published on this target`);
  48 |     expect(res.status(), `${SITEMAP} should answer 200`).toBe(200);
  49 |     const xml = await res.text();
  50 | 
  51 |     // A sitemap split past the 50,000-URL limit is an index: audit every child file
  52 |     const files = xml.includes('<sitemapindex') ? locs(xml).map(toRelativePath) : [SITEMAP];
  53 |     const problems: string[] = [];
  54 |     const listed: string[] = [];
  55 |     for (const file of files) {
  56 |       const body = file === SITEMAP ? xml : await (async () => {
  57 |         const r = await request.get(file);
  58 |         if (r.status() !== 200) problems.push(`${file}: sitemap file expected 200, got ${r.status()}`);
  59 |         return r.status() === 200 ? await r.text() : '';
  60 |       })();
  61 |       listed.push(...locs(body).map(toRelativePath));
  62 |     }
  63 |     const paths = [...new Set(listed)].filter(p => p !== '/districts/');
  64 |     expect(paths.length, 'districts sitemap should list URLs').toBeGreaterThan(0);
  65 | 
  66 |     for (const path of paths) {
  67 |       if (!DISTRICT_PATH.test(path)) problems.push(`${path}: not a lowercase /districts/<slug>/ URL`);
  68 |     }
  69 | 
  70 |     const results = await mapBounded(paths, CONCURRENCY, async (path) => {
  71 |       try {
  72 |         const r = await request.get(path, { maxRedirects: 0, timeout: 30000 });
  73 |         if (r.status() !== 200) return `${path}: expected 200, got ${r.status()}`;
  74 |         const canonical = canonicalPath(await r.text());
  75 |         if (canonical !== path) return `${path}: rel=canonical is ${canonical ?? 'missing'}`;
  76 |         return null;
  77 |       } catch (e) {
  78 |         return `${path}: request failed (${(e as Error).message.split('\n')[0]})`;
  79 |       }
  80 |     });
  81 |     problems.push(...results.filter((p): p is string => !!p));
  82 | 
  83 |     console.log(`Audited ${paths.length} district sitemap URLs, ${problems.length} problem(s)`);
> 84 |     expect(problems, `${problems.length} of ${paths.length} district sitemap URLs are stale or broken:\n${problems.join('\n')}`).toEqual([]);
     |                                                                                                                                  ^ Error: 1 of 7272 district sitemap URLs are stale or broken:
  85 |   });
  86 | });
  87 | 
```