| Section | Pass | Fail | Total | Rate |
|---|---|---|---|---|
| 1. Crawl Completion | 29 | 0 | 29 | 100% |
| 19. UI Tabs | 7 | 0 | 7 | 100% |
| 2. URL Count | 41 | 0 | 41 | 100% |
| 3. Config | 12 | 0 | 12 | 100% |
| 34. API Benchmarks | 10 | 0 | 10 | 100% |
| 35. Security | 4 | 0 | 4 | 100% |
| 40. HTTP Methods | 7 | 0 | 7 | 100% |
| 41. Sidebar Deep | 2 | 0 | 2 | 100% |
| Other | 3756 | 1 | 3757 | 100% |
| Endpoint | GET | POST | PUT | DEL |
|---|---|---|---|---|
| Crawl Status | ✓ | — | ✓ | ✓ |
| Project | ✓ | — | ✓ | ✓ |
| Projects List | ✓ | — | — | — |
| Charts | ✓ | — | — | — |
| Charts Warm | ✓ | — | — | — |
| Dates | ✓ | — | — | — |
| Share | ✓ | — | — | — |
| Project URLs | ✓ | — | — | — |
| Profile | ✓ | — | — | — |
| Crawl Data (POST) | ✓ | ✓ | — | — |
| Columns | ✓ | — | — | — |
| Insights | ✓ | — | — | — |
| Exports | ✓ | — | — | — |
The crawler does not set the canonical_not_match_url flag on canonical-other.html. The canonical tag value (https://nginx-website-for-test.ttfb.ovh/seo-tests/canonical-self.html) does not match the page URL, yet the crawler reports canonical_not_match_url=False. This means pages with incorrect canonical tags will not be flagged in SEO audits.
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Crawl status is terminal (completed or canceled) | status=canceled |
| ✓ PASS | is_complete flag is True or None (canceled) | is_complete=None |
| ✓ PASS | completed_at timestamp exists | completed_at=Fri, 03 Apr 2026 13:59:09 GMT |
| ✓ PASS | Processing event 'deployment' completed | |
| ✓ PASS | Processing event 'crawl' completed | |
| ✓ PASS | Processing event 'read_crawl_data' completed | |
| ✓ PASS | Processing event 'calculating_inlinks' completed | |
| ✓ PASS | Processing event 'calculating_pagerank' completed | |
| ✓ PASS | Processing event 'find_duplicate' completed | |
| ✓ PASS | Processing event 'canonical_and_redirect_lists' completed | |
| ✓ PASS | Processing event 'saving_results' completed | |
| ✓ PASS | All events in terminal state (completed/canceled) | |
| ✓ PASS | Crawl completed within 5 minutes | total=157s |
| ✓ PASS | Server deploy time < 5 minutes | deploy=140s |
| ✓ PASS | Status field 'is_complete' is boolean or None | type=NoneType, val=None |
| ✓ PASS | Status field 'completed_at' is string | type=str, len=29 |
| ✓ PASS | All events have parseable timestamps | parsed=8, total=8 |
| ✓ PASS | Processing events have read_crawl_data | read=2026-04-03 14:01:10+00:00 |
| ✓ PASS | Status completed_at is RFC date | ts=Fri, 03 Apr 2026 13:59:09 GMT |
| ✓ PASS | Crawl 7780e6a5 has 'is_complete' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl d5af37bd has 'is_complete' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl a161d2da has 'is_complete' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl status distribution counted | statuses={'quota limit reached': 5, 'completed': 2, 'canceled': 1} |
| ✓ PASS | completed_at >= created_at | diff=157s |
| ✓ PASS | Crawl duration: 157s | delta=157s |
| ✓ PASS | Project latest crawl has status or is_complete | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl has both created_at and completed_at | created=Fri, 03 Apr 2026 13:, completed=Fri, 03 Apr 2026 13: |
| ✓ PASS | Status created_at < completed_at | created=Fri, 03 Apr 2026 13:, completed=Fri, 03 Apr 2026 13: |
| ✓ PASS | Schema stable: status has url+events+is_complete | url=True, events=True, complete=True |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Crawl report page loads | |
| ✓ PASS | URL count displayed as 190 | 190 not found in page text |
| ✓ PASS | Site URL shown in header | |
| ✓ PASS | Sidebar nav: 'Overview' visible | |
| ✓ PASS | Sidebar nav: 'Indexability' visible | |
| ✓ PASS | Sidebar nav: 'Content' visible | |
| ✓ PASS | Sidebar nav: 'Links' visible | |
| ✓ PASS | Sidebar nav: 'Web Performance' visible | |
| ✓ PASS | Sidebar nav: 'Sitemaps' visible | |
| ✓ PASS | Sidebar nav: 'Bulk Exports' visible | |
| ✓ PASS | URL Explorer link visible | |
| ✓ PASS | 'Project Home' link exists | |
| ✓ PASS | Timeline shows deployment event | timeline mentions deploy |
| ✓ PASS | URL Explorer shows data table | |
| ✓ PASS | URL Explorer shows 190 URLs count | |
| ✓ PASS | URL Explorer has Export button | |
| ✓ PASS | URL Explorer has interactive controls (filter/toolbar) | buttons_found=1 |
| ✓ PASS | URL Explorer has Columns selector | |
| ✓ PASS | Sidebar 'Overview' tab loads content | keywords_found=['Status', 'URL'] |
| ✓ PASS | Sidebar 'Indexability' tab loads content | keywords_found=['Indexab'] |
| ✓ PASS | Sidebar 'Content' tab loads content | keywords_found=['Content'] |
| ✓ PASS | Sidebar 'Links' tab loads content | keywords_found=['Inlinks', 'Outlinks', 'link'] |
| ✓ PASS | Sidebar 'Sitemaps' tab loads content | keywords_found=['Sitemap', 'sitemap'] |
| ✓ PASS | Sidebar 'Web Performance' tab loads content | keywords_found=['Performance', 'ttfb'] |
| ✓ PASS | URL Explorer has pagination controls | |
| ✓ PASS | URL Explorer has column headers | th_count=0 |
| ✓ PASS | URL Explorer table has content | rows=0 |
| ✓ PASS | Crawl report has data tables | table_count=0 |
| ✓ PASS | Crawl report has chart/visualization elements | canvas=0, svg=0, chart_divs=2 |
| ✓ PASS | Title 'Crawl report': non-empty | title=Crawl report - nginx-website-for-test.tt |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Total tests >= 850 | count=875 |
| ✓ PASS | Final check: /crawl | has_json=True |
| ✓ PASS | Final check: /project | has_json=True |
| ✓ PASS | Final check: /projects | has_json=True |
| ✓ PASS | Final check: /crawl | has_json=False |
| ✓ PASS | Final check: /crawls | has_json=True |
| ✓ PASS | Final check: /crawl | has_json=True |
| ✓ PASS | Final check: /projects | has_json=True |
| ✓ PASS | Final check: /profile | has_json=True |
| ✓ PASS | Final check: /crawl | has_json=True |
| ✓ PASS | Final check: /crawls | has_json=True |
| ✓ PASS | Final check: /exports | has_json=True |
| ✓ PASS | 100 sections milestone reached | Sections 1-100 complete |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Target site loads | text_len=2052 |
| ✓ PASS | Target site has title | title=Home - nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Target site has navigation links | |
| ✓ PASS | Target site has sitemap.xml | text_len=5404 |
| ✓ PASS | Target site has robots.txt | text_len=551 |
| ✓ PASS | Target site has /seo-tests/ index | text_len=2254 |
| ✓ PASS | Target site /technology/ loads | text_len=2213 |
| ✓ PASS | Target site returns 200 | status=200 |
| ✓ PASS | Target site has security headers (HSTS or CSP) | headers=['content-encoding', 'content-security-policy', 'content-type', 'date', 'etag', 'last-modified', 'permissions-policy', 'referrer-policy'] |
| ✓ PASS | Target site has X-Frame-Options | xfo=True |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Pagination: p1 returns valid response | keys=['data', 'draw', 'recordsFiltered', 'recordsRelation', 'recordsTotal'] |
| ✓ PASS | Pagination: p2 returns valid response | keys=['data', 'draw', 'recordsFiltered', 'recordsRelation', 'recordsTotal'] |
| ✓ PASS | Pagination: recordsFiltered consistent | p1=190, p2=190 |
| ✓ PASS | Pagination: all 3 pages return data | p1=10, p2=10, p3=10 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST /project/schedule creates schedule | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Regex search returns response | type=dict |
| ✓ PASS | Empty regex search returns all records | total=190, expected=190 |
| ✓ PASS | Special chars search: no crash | type=dict |
| ✓ PASS | Special chars in search returns safe response | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Column search URL='technology': returns data | count=10 |
| ✓ PASS | Column search status='200': returns data | count=10 |
| ✓ PASS | Column search depth='1': returns data | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST /projects creates project | type=dict |
| ✓ PASS | New project has id | keys=['ok', 'project_id'] |
| ✓ PASS | GET new project returns data | name=qa-lifecycle-test-project |
| ✓ PASS | New project in projects list | found=True |
| ✓ PASS | DELETE project cleanup | resp={'message': 'No project IDs.', 'ok': False} |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST /schedule/ |
type=dict |
| ✓ PASS | Schedule speed updated | expected=slow, got=medium |
| ✓ PASS | Schedule speed reverted | reverted to medium |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Combined filter+order: returns data | count=10 |
| ✓ PASS | Insight filter indexable=true: returns data | count=10, total=190 |
| ✓ PASS | Filter depth=0: returns data | total=190 |
| ✓ PASS | Filter found_in_sitemaps=true: returns data | total=190 |
| ✓ PASS | Multi-filter status=200 + indexable: returns data | total=190 |
| ✓ PASS | Filter depth<=1: returns response | total=190, filtered=190 |
| ✓ PASS | Filter depth>=2: returns response | total=190, filtered=190 |
| ✓ PASS | Insight filter query=is returns response | type=dict |
| ✓ PASS | Insight filter query=missing returns response | type=dict |
| ✓ PASS | Insight filter query=contains returns response | type=dict |
| ✓ PASS | Insight filter query=prefix returns response | type=dict |
| ✓ PASS | Insight filter query=regex returns response | type=dict |
| ✓ PASS | Insight filter query=greater_equal returns response | type=dict |
| ✓ PASS | Insight filter query=less returns response | type=dict |
| ✓ PASS | Multi-filter crawl data returns response | type=dict |
| ✓ PASS | Multi-filter data has rows | count=10 |
| ✓ PASS | Insight filter types documented | types=none found |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Projects page shows project name | |
| ✓ PASS | Projects page shows crawl count |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | start=total_urls: returns response | data_count=10 |
| ✓ PASS | draw=0: returns response | data_count=10 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Create segment with multiple filters | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Content-type present: text-ratio-low.html | ct=text/html |
| ✓ PASS | HTML content-type: text-ratio-low.html | ct=text/html |
| ✓ PASS | Content-type present: canonical-other.html | ct=text/html |
| ✓ PASS | HTML content-type: canonical-other.html | ct=text/html |
| ✓ PASS | Content-type present: water-sports.html | ct=text/html |
| ✓ PASS | HTML content-type: water-sports.html | ct=text/html |
| ✓ PASS | Content-type present: gardening.html | ct=text/html |
| ✓ PASS | HTML content-type: gardening.html | ct=text/html |
| ✓ PASS | Content-type present: redirect-loop-target | ct=text/html |
| ✓ PASS | HTML content-type: redirect-loop-target | ct=text/html |
| ✓ PASS | Content-type present: homemade-pastry.html | ct=text/html |
| ✓ PASS | HTML content-type: homemade-pastry.html | ct=text/html |
| ✓ PASS | Content-type present: meta-desc-missing.ht | ct=text/html |
| ✓ PASS | HTML content-type: meta-desc-missing.ht | ct=text/html |
| ✓ PASS | Content-type present: meta-desc-duplicate- | ct=text/html |
| ✓ PASS | HTML content-type: meta-desc-duplicate- | ct=text/html |
| ✓ PASS | Content-type present: painting.html | ct=text/html |
| ✓ PASS | HTML content-type: painting.html | ct=text/html |
| ✓ PASS | Content-type present: root | ct=text/html |
| ✓ PASS | HTML content-type: root | ct=text/html |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Directory extracted: text-ratio-low.html | dir1=seo-tests |
| ✓ PASS | Hostname correct: text-ratio-low.html | hostname=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Directory extracted: canonical-other.html | dir1=seo-tests |
| ✓ PASS | Hostname correct: canonical-other.html | hostname=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Directory extracted: water-sports.html | dir1=sports |
| ✓ PASS | Hostname correct: water-sports.html | hostname=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Directory extracted: gardening.html | dir1=nature |
| ✓ PASS | Hostname correct: gardening.html | hostname=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Directory extracted: redirect-loop-target | dir1=seo-tests |
| ✓ PASS | Hostname correct: redirect-loop-target | hostname=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Directory extracted: homemade-pastry.html | dir1=cooking |
| ✓ PASS | Hostname correct: homemade-pastry.html | hostname=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Directory extracted: meta-desc-missing.ht | dir1=seo-tests |
| ✓ PASS | Hostname correct: meta-desc-missing.ht | hostname=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Directory extracted: meta-desc-duplicate- | dir1=seo-tests |
| ✓ PASS | Hostname correct: meta-desc-duplicate- | hostname=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Directory extracted: painting.html | dir1=art |
| ✓ PASS | Hostname correct: painting.html | hostname=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Directory extracted: root | dir1=art |
| ✓ PASS | Hostname correct: root | hostname=nginx-website-for-test.ttfb.ovh |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Indexable is bool: text-ratio-low.html | type=bool |
| ✓ PASS | Denied by robots bool: text-ratio-low.html | denied=False |
| ✓ PASS | Indexable is bool: canonical-other.html | type=bool |
| ✓ PASS | Non-indexable has reason: canonical-other.html | reason=Canonical |
| ✓ PASS | Denied by robots bool: canonical-other.html | denied=False |
| ✓ PASS | Indexable is bool: water-sports.html | type=bool |
| ✓ PASS | Denied by robots bool: water-sports.html | denied=False |
| ✓ PASS | Indexable is bool: gardening.html | type=bool |
| ✓ PASS | Denied by robots bool: gardening.html | denied=False |
| ✓ PASS | Indexable is bool: redirect-loop-target | type=bool |
| ✓ PASS | Denied by robots bool: redirect-loop-target | denied=False |
| ✓ PASS | Indexable is bool: homemade-pastry.html | type=bool |
| ✓ PASS | Denied by robots bool: homemade-pastry.html | denied=False |
| ✓ PASS | Indexable is bool: meta-desc-missing.ht | type=bool |
| ✓ PASS | Denied by robots bool: meta-desc-missing.ht | denied=False |
| ✓ PASS | Indexable is bool: meta-desc-duplicate- | type=bool |
| ✓ PASS | Denied by robots bool: meta-desc-duplicate- | denied=False |
| ✓ PASS | Indexable is bool: painting.html | type=bool |
| ✓ PASS | Denied by robots bool: painting.html | denied=False |
| ✓ PASS | Indexable is bool: root | type=bool |
| ✓ PASS | Denied by robots bool: root | denied=False |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Test suite approaching 1000 (currently 1007) | count=1007 |
| ✓ PASS | All API blueprints covered (auth, profile, project, crawl, chart) | 7 Flask blueprints tested |
| ✓ PASS | All CRUD operations covered | C=12, R=12+, U=9, D=9 across 53+ endpoints |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Project is_owner field exists | is_owner=True |
| ✓ PASS | Project is_owner is True (we own it) | is_owner=True |
| ✓ PASS | Project created_at exists | created_at=Fri, 03 Apr 2026 13:54:45 GMT |
| ✓ PASS | Project segment_count is int | segment_count=0 |
| ✓ PASS | Project created_at is ISO/RFC format | ts=Fri, 03 Apr 2026 13:54:45 GMT |
| ✓ PASS | Project created_at exists |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Crawl copy has ok field | ok=True |
| ✓ PASS | Copy contents is string | len=200748 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Crawler found 185+ pages | url_count=190 |
| ✓ PASS | Crawl: 180+ URLs are 2xx success | 2xx_count=185 |
| ✓ PASS | Crawl: image analysis section exists | count=2 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Halfway milestone: 50 iterations completed | iterations 3-50 |
| ✓ PASS | Halfway milestone: 1000+ tests | count=1020 |
| ✓ PASS | Halfway milestone: 100+ sections | 119 sections |
| ✓ PASS | Halfway milestone: 50+ endpoints | 53+ endpoints across 7 blueprints |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Exports page loads | |
| ✓ PASS | Exports API returns list | type= |
| ✓ PASS | Bulk Exports tab has export options | |
| ✓ PASS | Exports response is dict | type=dict |
| ✓ PASS | Exports 'exports' is list | type=list |
| ✓ PASS | Exports list has entries after CSV/XLSX trigger | count=6 |
| ✓ PASS | Page load < 10s: Exports | 969ms |
| ✓ PASS | Page load < 5s: Exports | 969ms |
| ✓ PASS | Exports: found completed exports | total=6, completed=6 |
| ✓ PASS | Category 'Exports': 7 endpoints | endpoints=7 |
| ✓ PASS | Title 'Exports': non-empty | title=Exports - Kelogs |
| ✓ PASS | Exports list is array | count=6 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Filter title missing: returns response | total=190, filtered=190 |
| ✓ PASS | Filter canonical missing: returns response | total=190, filtered=190 |
| ✓ PASS | Filter URL prefix seo-tests: returns response | total=190, filtered=190 |
| ✓ PASS | Filter URL contains technology: returns response | total=190, filtered=190 |
| ✓ PASS | Filter status is 200: returns response | total=190, filtered=190 |
| ✓ PASS | Filter canonical missing: returns response | filtered=190 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status created_at is RFC date | ts=Fri, 03 Apr 2026 13:56:32 GMT |
| ✓ PASS | Status updated_at is RFC date | ts=Wed, 08 Apr 2026 04:08:15 GMT |
| ✓ PASS | Status updated_at is string | val=Wed, 08 Apr 2026 04:08:15 |
| ✓ PASS | Status created_at is ISO date format | val=Fri, 03 Apr 2026 13:56:32 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | API v2 does not exist (404) | status=404 |
| ✓ PASS | API without version returns non-200 | status=404 |
| ✓ PASS | API v1 root returns response | status=404 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | 5 consecutive pages all return data | pages=5 |
| ✓ PASS | All 5 pages have data arrays | |
| ✓ PASS | 5 pages combined have records | total_records=50 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Segment group default reverted |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | All records have same crawl_id | unique_ids={'f86b84ae-fa69-424e-8a04-ec7515125950'} |
| ✓ PASS | Record crawl_id matches CRAWL_ID | data_id=f86b84ae-fa6 |
| ✓ PASS | All records have same hostname | hostnames={'nginx-website-for-test.ttfb.ovh'} |
| ✓ PASS | All URLs have same base | bases={'https://nginx-website-for-test.ttfb.ovh'} |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Unicode search: no crash | type=dict |
| ✓ PASS | Case search NGINX: returns response | |
| ✓ PASS | Case search nginx: returns response | |
| ✓ PASS | Long search (500 chars): no crash |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Projects has recent_crawls array | count=3 |
| ✓ PASS | Recent crawl has 'id' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Recent crawl has 'status' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Recent crawl has 'url' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Recent crawl has 'project_name' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Charts API returns data | type= |
| ✓ PASS | Charts response is dict | |
| ✓ PASS | Charts has response keys | keys=['_raw', '_status'] |
| ✓ PASS | Chart key '_raw' has value | type=str |
| ✓ PASS | Chart key '_status' has value | type=int |
| ✓ PASS | Charts warm API returns data | type= |
| ✓ PASS | Charts warm returns valid response | cold_keys=2, warm_keys=6 |
| ✓ PASS | Audit GET /crawl: Charts warm prev | has_data=True |
| ✓ PASS | Charts warm cache returns response | type=dict |
| ✓ PASS | Charts warm has data or ok | keys=['enable_js_rendering', 'has_mobile', 'has_segments', 'no_outlinks', 'ok', 'use_cache_header'] |
| ✓ PASS | Charts warm_previous returns response | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Error Invalid crawl UUID: returns response | has_status=True |
| ✓ PASS | Error Invalid project UUID: returns response | has_status=True |
| ✓ PASS | Error Invalid columns UUID: returns response | has_status=True |
| ✓ PASS | Error Invalid insights UUID: returns response | has_status=True |
| ✓ PASS | Error Invalid charts UUID: returns response | has_status=True |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Order by url desc: returns data | count=10 |
| ✓ PASS | Order by status desc: returns data | count=10 |
| ✓ PASS | Order by depth desc: returns data | count=10 |
| ✓ PASS | Order by pagerank desc: returns data | count=10 |
| ✓ PASS | Order by title desc: returns data | count=10 |
| ✓ PASS | Order by ttfb desc: returns data | count=10 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Headers is dict: text-ratio-low.html | type=dict |
| ✓ PASS | Headers is dict: canonical-other.html | type=dict |
| ✓ PASS | Headers is dict: water-sports.html | type=dict |
| ✓ PASS | Headers is dict: gardening.html | type=dict |
| ✓ PASS | Headers is dict: redirect-loop-target | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Round-trip 'total_rows': query returns data | insight_urls=188, filtered=190 |
| ✓ PASS | Round-trip 'url_http': query returns data | insight_urls=1, filtered=190 |
| ✓ PASS | Round-trip 'url_https': query returns data | insight_urls=187, filtered=190 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Date month: text-ratio-low. | val=2026-04 |
| ✓ PASS | Date day: text-ratio-low. | val=2026-04-03 |
| ✓ PASS | Date hour: text-ratio-low. | val=2026-04-03T13 |
| ✓ PASS | Date month: canonical-other | val=2026-04 |
| ✓ PASS | Date day: canonical-other | val=2026-04-03 |
| ✓ PASS | Date hour: canonical-other | val=2026-04-03T13 |
| ✓ PASS | Date month: water-sports.ht | val=2026-04 |
| ✓ PASS | Date day: water-sports.ht | val=2026-04-03 |
| ✓ PASS | Date hour: water-sports.ht | val=2026-04-03T13 |
| ✓ PASS | Date month: text-ratio | val=2026-04 |
| ✓ PASS | Date day: text-ratio | val=2026-04-03 |
| ✓ PASS | Date hour: text-ratio | val=2026-04-03T13 |
| ✓ PASS | Date month: canonical- | val=2026-04 |
| ✓ PASS | Date day: canonical- | val=2026-04-03 |
| ✓ PASS | Date hour: canonical- | val=2026-04-03T13 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | is_paginated bool: text-ratio-low. | val=False |
| ✓ PASS | is_paginated bool: canonical-other | val=False |
| ✓ PASS | is_paginated bool: water-sports.ht | val=False |
| ✓ PASS | is_paginated bool: gardening.html | val=False |
| ✓ PASS | is_paginated bool: redirect-loop-t | val=False |
| ✓ PASS | is_paginated bool: homemade-pastry | val=False |
| ✓ PASS | is_paginated bool: meta-desc-missi | val=False |
| ✓ PASS | is_paginated bool: meta-desc-dupli | val=False |
| ✓ PASS | is_paginated bool: painting.html | val=False |
| ✓ PASS | is_paginated bool: root | val=False |
| ✓ PASS | Col 'is_paginated' has data field | |
| ✓ PASS | Col 'is_paginated' has title | |
| ✓ PASS | Col 'is_paginated' has type | type=boolean |
| ✓ PASS | Col 'alternate_media' has data field | |
| ✓ PASS | Col 'alternate_media' has title | |
| ✓ PASS | Col 'alternate_media' has type | type=text |
| ✓ PASS | Bool col is_paginated: text-ratio-l | type=bool |
| ✓ PASS | Bool col is_paginated: canonical-ot | type=bool |
| ✓ PASS | Bool col is_paginated: water-sports | type=bool |
| ✓ PASS | Order asc is_paginated | ok=True |
| ✓ PASS | Order asc alternate_media | ok=True |
| ✓ PASS | Filter col 'is_paginated' in columns (Pagination) | exists=True |
| ✓ PASS | Has is_paginated: text-ratio | |
| ✓ PASS | Has is_paginated: canonical- | |
| ✓ PASS | Has is_paginated: water-spor | |
| ✓ PASS | Has is_paginated: gardening. | |
| ✓ PASS | Has is_paginated: redirect-l | |
| ✓ PASS | Has is_paginated: homemade-p | |
| ✓ PASS | Has is_paginated: meta-desc- | |
| ✓ PASS | Has is_paginated: meta-desc- | |
| ✓ PASS | Has is_paginated: painting.h | |
| ✓ PASS | Has is_paginated: root | |
| ✓ PASS | Bool3 is_paginated: text-rat | |
| ✓ PASS | Bool3 is_paginated: canonica |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Integration google_analytics: text-ratio-low. | type=bool, val=False |
| ✓ PASS | Integration google_analytics: text-ratio-low. | type=bool, val=False |
| ✓ PASS | Integration google_analytics: text-ratio-low. | type=bool, val=True |
| ✓ PASS | Integration search_console: text-ratio-low. | type=bool, val=False |
| ✓ PASS | Integration search_console: text-ratio-low. | type=bool, val=False |
| ✓ PASS | Integration search_console: text-ratio-low. | type=bool, val=True |
| ✓ PASS | Integration google_analytics: canonical-other | type=bool, val=False |
| ✓ PASS | Integration google_analytics: canonical-other | type=bool, val=False |
| ✓ PASS | Integration google_analytics: canonical-other | type=bool, val=True |
| ✓ PASS | Integration search_console: canonical-other | type=bool, val=False |
| ✓ PASS | Integration search_console: canonical-other | type=bool, val=False |
| ✓ PASS | Integration search_console: canonical-other | type=bool, val=True |
| ✓ PASS | Integration google_analytics: water-sports.ht | type=bool, val=False |
| ✓ PASS | Integration google_analytics: water-sports.ht | type=bool, val=False |
| ✓ PASS | Integration google_analytics: water-sports.ht | type=bool, val=True |
| ✓ PASS | Integration search_console: water-sports.ht | type=bool, val=False |
| ✓ PASS | Integration search_console: water-sports.ht | type=bool, val=False |
| ✓ PASS | Integration search_console: water-sports.ht | type=bool, val=True |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Redirects count valid: text-ratio-low. | num_redirects=0 |
| ✓ PASS | AMP is bool: text-ratio-low. | amp=False |
| ✓ PASS | Referer type: text-ratio-low. | ref=https://nginx-website-for-test |
| ✓ PASS | Redirects count valid: canonical-other | num_redirects=0 |
| ✓ PASS | AMP is bool: canonical-other | amp=False |
| ✓ PASS | Referer type: canonical-other | ref=https://nginx-website-for-test |
| ✓ PASS | Redirects count valid: water-sports.ht | num_redirects=0 |
| ✓ PASS | AMP is bool: water-sports.ht | amp=False |
| ✓ PASS | Referer type: water-sports.ht | ref=https://nginx-website-for-test |
| ✓ PASS | Redirects count valid: gardening.html | num_redirects=0 |
| ✓ PASS | AMP is bool: gardening.html | amp=False |
| ✓ PASS | Referer type: gardening.html | ref=https://nginx-website-for-test |
| ✓ PASS | Redirects count valid: redirect-loop-t | num_redirects=0 |
| ✓ PASS | AMP is bool: redirect-loop-t | amp=False |
| ✓ PASS | Referer type: redirect-loop-t | ref=https://nginx-website-for-test |
| ✓ PASS | Redirects count valid: homemade-pastry | num_redirects=0 |
| ✓ PASS | AMP is bool: homemade-pastry | amp=False |
| ✓ PASS | Referer type: homemade-pastry | ref=https://nginx-website-for-test |
| ✓ PASS | Redirects count valid: meta-desc-missi | num_redirects=0 |
| ✓ PASS | AMP is bool: meta-desc-missi | amp=False |
| ✓ PASS | Referer type: meta-desc-missi | ref=https://nginx-website-for-test |
| ✓ PASS | Redirects count valid: meta-desc-dupli | num_redirects=0 |
| ✓ PASS | AMP is bool: meta-desc-dupli | amp=False |
| ✓ PASS | Referer type: meta-desc-dupli | ref=https://nginx-website-for-test |
| ✓ PASS | Redirects count valid: painting.html | num_redirects=0 |
| ✓ PASS | AMP is bool: painting.html | amp=False |
| ✓ PASS | Referer type: painting.html | ref=https://nginx-website-for-test |
| ✓ PASS | Redirects count valid: root | num_redirects=0 |
| ✓ PASS | AMP is bool: root | amp=False |
| ✓ PASS | Referer type: root | ref=https://nginx-website-for-test |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Share API returns response | |
| ✓ PASS | Share has 'ok' field | keys=['ok', 'share'] |
| ✓ PASS | Share ok is boolean True | ok=True |
| ✓ PASS | Share has 'share' field | keys=['ok', 'share'] |
| ✓ PASS | Share value is valid (bool, None, or dict with token) | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | title_same_as_h1 bool: text-ratio-low. | val=False |
| ✓ PASS | dup_idx level valid: text-ratio-low. | level=0 |
| ✓ PASS | dup_idx title bool: text-ratio-low. | val=False |
| ✓ PASS | title_same_as_h1 bool: canonical-other | val=False |
| ✓ PASS | dup_idx level valid: canonical-other | level=0 |
| ✓ PASS | dup_idx title bool: canonical-other | val=False |
| ✓ PASS | title_same_as_h1 bool: water-sports.ht | val=False |
| ✓ PASS | dup_idx level valid: water-sports.ht | level=0 |
| ✓ PASS | dup_idx title bool: water-sports.ht | val=False |
| ✓ PASS | title_same_as_h1 bool: gardening.html | val=False |
| ✓ PASS | dup_idx level valid: gardening.html | level=0 |
| ✓ PASS | dup_idx title bool: gardening.html | val=False |
| ✓ PASS | title_same_as_h1 bool: redirect-loop-t | val=False |
| ✓ PASS | dup_idx level valid: redirect-loop-t | level=0 |
| ✓ PASS | dup_idx title bool: redirect-loop-t | val=False |
| ✓ PASS | Col 'title_same_as_h1' has data field | |
| ✓ PASS | Col 'title_same_as_h1' has title | |
| ✓ PASS | Col 'title_same_as_h1' has type | type=boolean |
| ✓ PASS | Bool col title_same_as_h1: text-ratio-l | type=bool |
| ✓ PASS | Bool col title_same_as_h1: canonical-ot | type=bool |
| ✓ PASS | Bool col title_same_as_h1: water-sports | type=bool |
| ✓ PASS | Order asc title_same_as_h1 | ok=True |
| ✓ PASS | title_same_as_h1 accurate: text-ratio-l | flag=False, actual=False |
| ✓ PASS | title_same_as_h1 accurate: canonical-ot | flag=False, actual=False |
| ✓ PASS | title_same_as_h1 accurate: water-sports | flag=False, actual=False |
| ✓ PASS | title_same_as_h1 accurate: gardening.ht | flag=False, actual=False |
| ✓ PASS | title_same_as_h1 accurate: redirect-loo | flag=False, actual=False |
| ✓ PASS | Has dup_idx_level: text-ratio | |
| ✓ PASS | Has dup_idx_level: canonical- | |
| ✓ PASS | Has dup_idx_level: water-spor | |
| ✓ PASS | Has dup_idx_level: gardening. | |
| ✓ PASS | Has dup_idx_level: redirect-l | |
| ✓ PASS | Has dup_idx_level: homemade-p | |
| ✓ PASS | Has dup_idx_level: meta-desc- | |
| ✓ PASS | Has dup_idx_level: meta-desc- | |
| ✓ PASS | Has dup_idx_level: painting.h | |
| ✓ PASS | Has dup_idx_level: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Global+column search: returns response | total=190 |
| ✓ PASS | Global+column search: has data | count=10 |
| ✓ PASS | Search+order: returns response | count=10 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Cache: status crawl_id identical | |
| ✓ PASS | Cache: status project_name identical | |
| ✓ PASS | Cache: first record same URL | url1=https://nginx-website-for-test |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Dates user_agent == Status user_agent | dates=desktop, status=desktop |
| ✓ PASS | Dates 'has_ga' is boolean | has_ga=False |
| ✓ PASS | Dates 'has_log' is boolean | has_log=False |
| ✓ PASS | Dates 'has_webmasters' is boolean | has_webmasters=False |
| ✓ PASS | Dates 'date' is valid string | date=Fri, 03 Apr 2026 13:56:32 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Filter meta_desc missing: returns response | total=190, filtered=190 |
| ✓ PASS | Filter h1 missing: returns response | filtered=190 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Insight All/total_rows has data | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Insight URLs Containing Non-standard Characters/url_non_ascii has data | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Insight URLs Containing Non-standard Characters/url_underscores has data | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Insight URLs Containing Non-standard Characters/url_uppercases has data | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Status code URLs sum ~ total | sc_sum=188 |
| ✓ PASS | Insight All/total_rows urls >= 0 | urls=188 |
| ✓ PASS | Insight URLs Containing Non-standard Characters/url_non_ascii urls >= 0 | urls=0 |
| ✓ PASS | Insight URLs Containing Non-standard Characters/url_underscores urls >= 0 | urls=0 |
| ✓ PASS | Insight URLs Containing Non-standard Characters/url_uppercases urls >= 0 | urls=0 |
| ✓ PASS | Insight Status Codes/url_success urls >= 0 | urls=185 |
| ✓ PASS | Insight Status Codes/url_redirect urls >= 0 | urls=1 |
| ✓ PASS | Insight Status Codes/url_client_erro urls >= 0 | urls=2 |
| ✓ PASS | Insight Status Codes/url_server_erro urls >= 0 | urls=0 |
| ✓ PASS | Insight Page Titles/url_title_missi urls >= 0 | urls=2 |
| ✓ PASS | Insight Page Titles/url_title_dupli urls >= 0 | urls=6 |
| ✓ PASS | Insight Page Titles/url_title_long urls >= 0 | urls=4 |
| ✓ PASS | Insight Page Titles/url_title_short urls >= 0 | urls=36 |
| ✓ PASS | Insight Status code by Depth/url_status_dept urls >= 0 | urls=11 |
| ✓ PASS | Insight Status code by Depth/url_status_dept urls >= 0 | urls=172 |
| ✓ PASS | Insight Status code by Depth/url_status_dept urls >= 0 | urls=4 |
| ✓ PASS | Insight Status code by Depth/url_status_dept urls >= 0 | urls=0 |
| ✓ PASS | Insight Status code by Depth/url_status_dept urls >= 0 | urls=0 |
| ✓ PASS | Insight Meta Description/url_meta_descri urls >= 0 | urls=113 |
| ✓ PASS | Insight Meta Description/url_meta_descri urls >= 0 | urls=6 |
| ✓ PASS | Insight Meta Description/url_meta_descri urls >= 0 | urls=1 |
| ✓ PASS | Insight Meta Description/url_meta_descri urls >= 0 | urls=66 |
| ✓ PASS | Insight H1/url_h1_missing urls >= 0 | urls=4 |
| ✓ PASS | Insight H1/url_h1_duplicat urls >= 0 | urls=6 |
| ✓ PASS | Insight H1/url_h1_over_70 urls >= 0 | urls=1 |
| ✓ PASS | Insight H1/url_title_same_ urls >= 0 | urls=1 |
| ✓ PASS | Insight Canonical/url_canonical_p urls >= 0 | urls=73 |
| ✓ PASS | Insight Canonical/url_canonical_n urls >= 0 | urls=0 |
| ✓ PASS | Insight Canonical/url_canonical_m urls >= 0 | urls=112 |
| ✓ PASS | Insight Canonical/url_canonical_n urls >= 0 | urls=7 |
| ✓ PASS | Insight Directives Robots/url_directive_i urls >= 0 | urls=5 |
| ✓ PASS | Insight Directives Robots/url_directive_n urls >= 0 | urls=1 |
| ✓ PASS | Insight Directives Robots/url_directive_f urls >= 0 | urls=5 |
| ✓ PASS | Insight Directives Robots/url_directive_n urls >= 0 | urls=1 |
| ✓ PASS | Insight Directives Robots/url_directive_n urls >= 0 | urls=178 |
| ✓ PASS | Insight Hreflang/url_hreflang_pr urls >= 0 | urls=5 |
| ✓ PASS | Insight Hreflang/url_hreflang_mi urls >= 0 | urls=180 |
| ✓ PASS | Insight Pagination/url_paginated urls >= 0 | urls=3 |
| ✓ PASS | Insight Images/url_images_miss urls >= 0 | urls=0 |
| ✓ PASS | Insight Images/url_images_alt_ urls >= 0 | urls=3 |
| ✓ PASS | Insight Performance/url_ttfb_over_6 urls >= 0 | urls=0 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Filter status!=200: returns response | total=190, filtered=190 |
| ✓ PASS | Filter not indexable: returns response | total=190, filtered=190 |
| ✓ PASS | Filter title present: returns response | total=190, filtered=190 |
| ✓ PASS | Filter URL regex seo: returns response | total=190, filtered=190 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Triple filter (200+indexable+depth<=2): returns data | total=190, filtered=190 |
| ✓ PASS | Triple filter (canonical+sitemaps+follow): returns data | filtered=190 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Schedule config 'url' | val=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Schedule config 'protocol' | val=https |
| ✓ PASS | Schedule config 'user_agent' | val=desktop |
| ✓ PASS | Schedule config 'speed' | val=medium |
| ✓ PASS | Schedule config 'enable_js_rendering' | val=False |
| ✓ PASS | Schedule config 'max_pages' | val=None |
| ✓ PASS | Schedule config 'max_depth' | val=10 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status project_id matches | status_pid=a74b27ce-68b3-44a7-9db9-18bc6f75b419 |
| ✓ PASS | Columns count > 20 (rich schema) | count=71 |
| ✓ PASS | Status project_id is valid UUID format | project_id=a74b27ce-68b3-44a7-9db9-18bc6f75b419 |
| ✓ PASS | Status project_id matches test constant | status_pid=a74b27ce-68b |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | accessed_at exists: text-ratio-low. | val=2026-04-03T13:58:58 |
| ✓ PASS | accessed_at is datetime: text-ratio-low. | val=2026-04-03T13:58:58 |
| ✓ PASS | accessed_at exists: canonical-other | val=2026-04-03T13:59:00 |
| ✓ PASS | accessed_at is datetime: canonical-other | val=2026-04-03T13:59:00 |
| ✓ PASS | accessed_at exists: water-sports.ht | val=2026-04-03T13:58:59 |
| ✓ PASS | accessed_at is datetime: water-sports.ht | val=2026-04-03T13:58:59 |
| ✓ PASS | accessed_at exists: gardening.html | val=2026-04-03T13:59:00 |
| ✓ PASS | accessed_at is datetime: gardening.html | val=2026-04-03T13:59:00 |
| ✓ PASS | accessed_at exists: redirect-loop-t | val=2026-04-03T13:58:58 |
| ✓ PASS | accessed_at is datetime: redirect-loop-t | val=2026-04-03T13:58:58 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Col 'url' has data field | |
| ✓ PASS | Col 'url' has title | |
| ✓ PASS | Col 'url' has type | type=text |
| ✓ PASS | Col 'status' has data field | |
| ✓ PASS | Col 'status' has title | |
| ✓ PASS | Col 'status' has type | type=long |
| ✓ PASS | Col 'depth' has data field | |
| ✓ PASS | Col 'depth' has title | |
| ✓ PASS | Col 'depth' has type | type=long |
| ✓ PASS | Col 'pagerank' has data field | |
| ✓ PASS | Col 'pagerank' has title | |
| ✓ PASS | Col 'pagerank' has type | type=float |
| ✓ PASS | Col 'duplicate.level' has data field | |
| ✓ PASS | Col 'duplicate.level' has title | |
| ✓ PASS | Col 'duplicate.level' has type | type=long |
| ✓ PASS | Col 'duplicate_indexable.level' has data field | |
| ✓ PASS | Col 'duplicate_indexable.level' has title | |
| ✓ PASS | Col 'duplicate_indexable.level' has type | type=long |
| ✓ PASS | Col 'title' has data field | |
| ✓ PASS | Col 'title' has title | |
| ✓ PASS | Col 'title' has type | type=text |
| ✓ PASS | Col 'title_length' has data field | |
| ✓ PASS | Col 'title_length' has title | |
| ✓ PASS | Col 'title_length' has type | type=long |
| ✓ PASS | Col 'duplicate.title.bool' has data field | |
| ✓ PASS | Col 'duplicate.title.bool' has title | |
| ✓ PASS | Col 'duplicate.title.bool' has type | type=boolean |
| ✓ PASS | Col 'duplicate.title.count' has data field | |
| ✓ PASS | Col 'duplicate.title.count' has title | |
| ✓ PASS | Col 'duplicate.title.count' has type | type=long |
| ✓ PASS | Col 'duplicate_indexable.title' has data field | |
| ✓ PASS | Col 'duplicate_indexable.title' has title | |
| ✓ PASS | Col 'duplicate_indexable.title' has type | type=boolean |
| ✓ PASS | Col 'duplicate_indexable.title' has data field | |
| ✓ PASS | Col 'duplicate_indexable.title' has title | |
| ✓ PASS | Col 'duplicate_indexable.title' has type | type=long |
| ✓ PASS | Col 'accessed_at' has data field | |
| ✓ PASS | Col 'accessed_at' has title | |
| ✓ PASS | Col 'accessed_at' has type | type=date |
| ✓ PASS | Col 'meta_description' has data field | |
| ✓ PASS | Col 'meta_description' has title | |
| ✓ PASS | Col 'meta_description' has type | type=text |
| ✓ PASS | Col 'meta_description_length' has data field | |
| ✓ PASS | Col 'meta_description_length' has title | |
| ✓ PASS | Col 'meta_description_length' has type | type=long |
| ✓ PASS | Col 'duplicate.meta_descriptio' has data field | |
| ✓ PASS | Col 'duplicate.meta_descriptio' has title | |
| ✓ PASS | Col 'duplicate.meta_descriptio' has type | type=boolean |
| ✓ PASS | Col 'duplicate.meta_descriptio' has data field | |
| ✓ PASS | Col 'duplicate.meta_descriptio' has title | |
| ✓ PASS | Col 'duplicate.meta_descriptio' has type | type=long |
| ✓ PASS | Col 'duplicate_indexable.meta_' has data field | |
| ✓ PASS | Col 'duplicate_indexable.meta_' has title | |
| ✓ PASS | Col 'duplicate_indexable.meta_' has type | type=boolean |
| ✓ PASS | Col 'duplicate_indexable.meta_' has data field | |
| ✓ PASS | Col 'duplicate_indexable.meta_' has title | |
| ✓ PASS | Col 'duplicate_indexable.meta_' has type | type=long |
| ✓ PASS | Col 'ttfb' has data field | |
| ✓ PASS | Col 'ttfb' has title | |
| ✓ PASS | Col 'ttfb' has type | type=float |
| ✓ PASS | Col 'referer' has data field | |
| ✓ PASS | Col 'referer' has title | |
| ✓ PASS | Col 'referer' has type | type=text |
| ✓ PASS | Col 'h1_first' has data field | |
| ✓ PASS | Col 'h1_first' has title | |
| ✓ PASS | Col 'h1_first' has type | type=text |
| ✓ PASS | Col 'h1_first_length' has data field | |
| ✓ PASS | Col 'h1_first_length' has title | |
| ✓ PASS | Col 'h1_first_length' has type | type=long |
| ✓ PASS | Col 'duplicate.h1_first.bool' has data field | |
| ✓ PASS | Col 'duplicate.h1_first.bool' has title | |
| ✓ PASS | Col 'duplicate.h1_first.bool' has type | type=boolean |
| ✓ PASS | Col 'duplicate.h1_first.count' has data field | |
| ✓ PASS | Col 'duplicate.h1_first.count' has title | |
| ✓ PASS | Col 'duplicate.h1_first.count' has type | type=long |
| ✓ PASS | Col 'duplicate_indexable.h1_fi' has data field | |
| ✓ PASS | Col 'duplicate_indexable.h1_fi' has title | |
| ✓ PASS | Col 'duplicate_indexable.h1_fi' has type | type=boolean |
| ✓ PASS | Col 'duplicate_indexable.h1_fi' has data field | |
| ✓ PASS | Col 'duplicate_indexable.h1_fi' has title | |
| ✓ PASS | Col 'duplicate_indexable.h1_fi' has type | type=long |
| ✓ PASS | Col 'imgs_without_alt' has data field | |
| ✓ PASS | Col 'imgs_without_alt' has title | |
| ✓ PASS | Col 'imgs_without_alt' has type | type=long |
| ✓ PASS | Col 'canonical_not_match_url' has data field | |
| ✓ PASS | Col 'canonical_not_match_url' has title | |
| ✓ PASS | Col 'canonical_not_match_url' has type | type=boolean |
| ✓ PASS | Col 'follow' has data field | |
| ✓ PASS | Col 'follow' has title | |
| ✓ PASS | Col 'follow' has type | type=boolean |
| ✓ PASS | Col 'denied_by_robots_txt' has data field | |
| ✓ PASS | Col 'denied_by_robots_txt' has title | |
| ✓ PASS | Col 'denied_by_robots_txt' has type | type=boolean |
| ✓ PASS | Col 'size' has data field | |
| ✓ PASS | Col 'size' has title | |
| ✓ PASS | Col 'size' has type | type=long |
| ✓ PASS | Col 'indexable_bool' has data field | |
| ✓ PASS | Col 'indexable_bool' has title | |
| ✓ PASS | Col 'indexable_bool' has type | type=boolean |
| ✓ PASS | Col 'indexable_blocked_reason' has data field | |
| ✓ PASS | Col 'indexable_blocked_reason' has title | |
| ✓ PASS | Col 'indexable_blocked_reason' has type | type=text |
| ✓ PASS | Col 'meta_robots' has data field | |
| ✓ PASS | Col 'meta_robots' has title | |
| ✓ PASS | Col 'meta_robots' has type | type=text |
| ✓ PASS | Col 'found_in_sitemaps' has data field | |
| ✓ PASS | Col 'found_in_sitemaps' has title | |
| ✓ PASS | Col 'found_in_sitemaps' has type | type=boolean |
| ✓ PASS | Col 'found_only_in_sitemaps' has data field | |
| ✓ PASS | Col 'found_only_in_sitemaps' has title | |
| ✓ PASS | Col 'found_only_in_sitemaps' has type | type=boolean |
| ✓ PASS | Col 'discovery_source' has data field | |
| ✓ PASS | Col 'discovery_source' has title | |
| ✓ PASS | Col 'discovery_source' has type | type=text |
| ✓ PASS | Col 'hreflang_bidirectional' has data field | |
| ✓ PASS | Col 'hreflang_bidirectional' has title | |
| ✓ PASS | Col 'hreflang_bidirectional' has type | type=boolean |
| ✓ PASS | Col 'amp' has data field | |
| ✓ PASS | Col 'amp' has title | |
| ✓ PASS | Col 'amp' has type | type=boolean |
| ✓ PASS | Col 'data_in_both_crawl_and_go' has data field | |
| ✓ PASS | Col 'data_in_both_crawl_and_go' has title | |
| ✓ PASS | Col 'data_in_both_crawl_and_go' has type | type=boolean |
| ✓ PASS | Col 'data_only_in_google_analy' has data field | |
| ✓ PASS | Col 'data_only_in_google_analy' has title | |
| ✓ PASS | Col 'data_only_in_google_analy' has type | type=boolean |
| ✓ PASS | Col 'data_only_in_crawl_and_no' has data field | |
| ✓ PASS | Col 'data_only_in_crawl_and_no' has title | |
| ✓ PASS | Col 'data_only_in_crawl_and_no' has type | type=boolean |
| ✓ PASS | Col 'data_in_both_crawl_and_se' has data field | |
| ✓ PASS | Col 'data_in_both_crawl_and_se' has title | |
| ✓ PASS | Col 'data_in_both_crawl_and_se' has type | type=boolean |
| ✓ PASS | Col 'data_only_in_search_conso' has data field | |
| ✓ PASS | Col 'data_only_in_search_conso' has title | |
| ✓ PASS | Col 'data_only_in_search_conso' has type | type=boolean |
| ✓ PASS | Col 'data_only_in_crawl_and_no' has data field | |
| ✓ PASS | Col 'data_only_in_crawl_and_no' has title | |
| ✓ PASS | Col 'data_only_in_crawl_and_no' has type | type=boolean |
| ✓ PASS | Col 'url_directory_1' has data field | |
| ✓ PASS | Col 'url_directory_1' has title | |
| ✓ PASS | Col 'url_directory_1' has type | type=text |
| ✓ PASS | Col 'url_directory_2' has data field | |
| ✓ PASS | Col 'url_directory_2' has title | |
| ✓ PASS | Col 'url_directory_2' has type | type=text |
| ✓ PASS | Col 'canonical_link_tag' has data field | |
| ✓ PASS | Col 'canonical_link_tag' has title | |
| ✓ PASS | Col 'canonical_link_tag' has type | type=text |
| ✓ PASS | Col 'final_destination_url' has data field | |
| ✓ PASS | Col 'final_destination_url' has title | |
| ✓ PASS | Col 'final_destination_url' has type | type=text |
| ✓ PASS | Col 'num_redirects' has data field | |
| ✓ PASS | Col 'num_redirects' has title | |
| ✓ PASS | Col 'num_redirects' has type | type=long |
| ✓ PASS | Col 'redirect_list' has data field | |
| ✓ PASS | Col 'redirect_list' has title | |
| ✓ PASS | Col 'redirect_list' has type | type=None |
| ✓ PASS | Col 'hostname' has data field | |
| ✓ PASS | Col 'hostname' has title | |
| ✓ PASS | Col 'hostname' has type | type=text |
| ✓ PASS | Col 'hreflang_count' has data field | |
| ✓ PASS | Col 'hreflang_count' has title | |
| ✓ PASS | Col 'hreflang_count' has type | type=long |
| ✓ PASS | Col 'found_in_crawl' has data field | |
| ✓ PASS | Col 'found_in_crawl' has title | |
| ✓ PASS | Col 'found_in_crawl' has type | type=boolean |
| ✓ PASS | Col 'found_in_logs' has data field | |
| ✓ PASS | Col 'found_in_logs' has title | |
| ✓ PASS | Col 'found_in_logs' has type | type=boolean |
| ✓ PASS | Col 'found_in_analytics' has data field | |
| ✓ PASS | Col 'found_in_analytics' has title | |
| ✓ PASS | Col 'found_in_analytics' has type | type=boolean |
| ✓ PASS | Col 'found_in_search_console' has data field | |
| ✓ PASS | Col 'found_in_search_console' has title | |
| ✓ PASS | Col 'found_in_search_console' has type | type=boolean |
| ✓ PASS | Col 'hreflang.de' has data field | |
| ✓ PASS | Col 'hreflang.de' has title | |
| ✓ PASS | Col 'hreflang.de' has type | type=text |
| ✓ PASS | Col 'hreflang.en' has data field | |
| ✓ PASS | Col 'hreflang.en' has title | |
| ✓ PASS | Col 'hreflang.en' has type | type=text |
| ✓ PASS | Col 'hreflang.es' has data field | |
| ✓ PASS | Col 'hreflang.es' has title | |
| ✓ PASS | Col 'hreflang.es' has type | type=text |
| ✓ PASS | Col 'hreflang.fr' has data field | |
| ✓ PASS | Col 'hreflang.fr' has title | |
| ✓ PASS | Col 'hreflang.fr' has type | type=text |
| ✓ PASS | Col 'hreflang.x-default' has data field | |
| ✓ PASS | Col 'hreflang.x-default' has title | |
| ✓ PASS | Col 'hreflang.x-default' has type | type=text |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Total unique endpoints tested: 63 | count=63 |
| ✓ PASS | Category 'Auth': 4 endpoints | endpoints=4 |
| ✓ PASS | Category 'Projects': 4 endpoints | endpoints=4 |
| ✓ PASS | Category 'Segments': 6 endpoints | endpoints=6 |
| ✓ PASS | Category 'Schedules': 3 endpoints | endpoints=3 |
| ✓ PASS | Category 'Alerts': 2 endpoints | endpoints=2 |
| ✓ PASS | Category 'Members': 1 endpoints | endpoints=1 |
| ✓ PASS | Category 'Crawl': 6 endpoints | endpoints=6 |
| ✓ PASS | Category 'Data': 8 endpoints | endpoints=8 |
| ✓ PASS | Category 'Reports': 4 endpoints | endpoints=4 |
| ✓ PASS | Category 'Compare': 5 endpoints | endpoints=5 |
| ✓ PASS | Category 'Share': 2 endpoints | endpoints=2 |
| ✓ PASS | Category 'Charts': 3 endpoints | endpoints=3 |
| ✓ PASS | Category 'Integrations': 4 endpoints | endpoints=4 |
| ✓ PASS | Category 'Utils': 1 endpoints | endpoints=1 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Max complexity query: returns response | total=190 |
| ✓ PASS | Max complexity query: has data array | count=10 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | 200→size>0: text-ratio-low. | status=200, size=2544 |
| ✓ PASS | 200→has title: text-ratio-low. | title=yes |
| ✓ PASS | Crawled page found_in_crawl=True: text-ratio-low. | found_in_crawl=True |
| ✓ PASS | Canonical → not_match is bool: text-ratio-low. | cnm=False |
| ✓ PASS | 200→size>0: canonical-other | status=200, size=1804 |
| ✓ PASS | 200→has title: canonical-other | title=yes |
| ✓ PASS | Crawled page found_in_crawl=True: canonical-other | found_in_crawl=True |
| ✓ PASS | Canonical → not_match is bool: canonical-other | cnm=False |
| ✓ PASS | 200→size>0: water-sports.ht | status=200, size=4094 |
| ✓ PASS | 200→has title: water-sports.ht | title=yes |
| ✓ PASS | Crawled page found_in_crawl=True: water-sports.ht | found_in_crawl=True |
| ✓ PASS | 200→size>0: gardening.html | status=200, size=4014 |
| ✓ PASS | 200→has title: gardening.html | title=yes |
| ✓ PASS | Crawled page found_in_crawl=True: gardening.html | found_in_crawl=True |
| ✓ PASS | 200→size>0: redirect-loop-t | status=200, size=1437 |
| ✓ PASS | 200→has title: redirect-loop-t | title=yes |
| ✓ PASS | Crawled page found_in_crawl=True: redirect-loop-t | found_in_crawl=True |
| ✓ PASS | Canonical → not_match is bool: redirect-loop-t | cnm=False |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Cache headers crawl: has content-type | headers=['content-encoding', 'content-type', 'date', 'server', 'vary'] |
| ✓ PASS | Cache headers crawl: has cache hint | cache_headers=[] |
| ✓ PASS | Cache headers profile: has content-type | headers=['content-length', 'content-type', 'date', 'server', 'vary'] |
| ✓ PASS | Cache headers profile: has cache hint | cache_headers=[] |
| ✓ PASS | Cache headers crawl: has content-type | headers=['content-encoding', 'content-type', 'date', 'server', 'vary'] |
| ✓ PASS | Cache headers crawl: has cache hint | cache_headers=[] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Events count is 8-9 (standard pipeline) | count=8 |
| ✓ PASS | has_segments matches segment_count > 0 | has_segments=False, segment_count=0 |
| ✓ PASS | Status has_preprocess_rules is boolean | val=False |
| ✓ PASS | Status has_scrape_rules is boolean | val=False |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Type url=text: text-ratio-l | expected=text, got=str |
| ✓ PASS | Type status=long: text-ratio-l | expected=long, got=int |
| ✓ PASS | Type depth=long: text-ratio-l | expected=long, got=int |
| ✓ PASS | Type pagerank=float: text-ratio-l | expected=float, got=float |
| ✓ PASS | Type duplicate.level=long: text-ratio-l | expected=long, got=int |
| ✓ PASS | Type duplicate_indexable.level=long: text-ratio-l | expected=long, got=int |
| ✓ PASS | Type title=text: text-ratio-l | expected=text, got=str |
| ✓ PASS | Type duplicate.title.bool=boolean: text-ratio-l | expected=boolean, got=bool |
| ✓ PASS | Type duplicate_indexable.title.bool=boolean: text-ratio-l | expected=boolean, got=bool |
| ✓ PASS | Type accessed_at=date: text-ratio-l | expected=date, got=str |
| ✓ PASS | Type meta_description=text: text-ratio-l | expected=text, got=str |
| ✓ PASS | Type url=text: canonical-ot | expected=text, got=str |
| ✓ PASS | Type status=long: canonical-ot | expected=long, got=int |
| ✓ PASS | Type depth=long: canonical-ot | expected=long, got=int |
| ✓ PASS | Type pagerank=float: canonical-ot | expected=float, got=float |
| ✓ PASS | Type duplicate.level=long: canonical-ot | expected=long, got=int |
| ✓ PASS | Type duplicate_indexable.level=long: canonical-ot | expected=long, got=int |
| ✓ PASS | Type title=text: canonical-ot | expected=text, got=str |
| ✓ PASS | Type duplicate.title.bool=boolean: canonical-ot | expected=boolean, got=bool |
| ✓ PASS | Type duplicate_indexable.title.bool=boolean: canonical-ot | expected=boolean, got=bool |
| ✓ PASS | Type accessed_at=date: canonical-ot | expected=date, got=str |
| ✓ PASS | Type meta_description=text: canonical-ot | expected=text, got=str |
| ✓ PASS | Type url=text: water-sports | expected=text, got=str |
| ✓ PASS | Type status=long: water-sports | expected=long, got=int |
| ✓ PASS | Type depth=long: water-sports | expected=long, got=int |
| ✓ PASS | Type pagerank=float: water-sports | expected=float, got=float |
| ✓ PASS | Type duplicate.level=long: water-sports | expected=long, got=int |
| ✓ PASS | Type duplicate_indexable.level=long: water-sports | expected=long, got=int |
| ✓ PASS | Type title=text: water-sports | expected=text, got=str |
| ✓ PASS | Type duplicate.title.bool=boolean: water-sports | expected=boolean, got=bool |
| ✓ PASS | Type duplicate_indexable.title.bool=boolean: water-sports | expected=boolean, got=bool |
| ✓ PASS | Type accessed_at=date: water-sports | expected=date, got=str |
| ✓ PASS | Type 'float' has 3 columns | |
| ✓ PASS | Type 'text' has 19 columns | |
| ✓ PASS | Type 'long' has 21 columns | |
| ✓ PASS | Type 'boolean' has 26 columns | |
| ✓ PASS | Type 'date' has 1 columns | |
| ✓ PASS | Data catalog: 7 NoneType fields | sample=['duplicate.h1_first.count', 'duplicate.meta_description.count', 'duplicate.title.count', 'duplicate_indexable.h1_first.count'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Group 'Group 1' has id | |
| ✓ PASS | Group 'Group 1' has name | |
| ✓ PASS | Group 'Group 1' has default flag | is_default=True, default=True |
| ✓ PASS | Group 'qa-test-group' has id | |
| ✓ PASS | Group 'qa-test-group' has name | |
| ✓ PASS | Group 'qa-test-group' has default flag | is_default=False, default=False |
| ✓ PASS | Group 'qa-test-group' has id | |
| ✓ PASS | Group 'qa-test-group' has name | |
| ✓ PASS | Group 'qa-test-group' has default flag | is_default=False, default=False |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | 10 concurrent: all complete | got=10 |
| ✓ PASS | 10 concurrent: all return 200 | 10/10 |
| ✓ PASS | 10 concurrent: total < 10s | ms=296 |
| ✓ PASS | 10 concurrent: avg < 2s per request | avg=30ms |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Page 2 returns data | got=10 |
| ✓ PASS | Page 2 pagination response valid | overlap=10/10 (free tier may ignore offset) |
| ✓ PASS | length=1 returns data (free tier: always 10) | got=10 |
| ✓ PASS | Search 'seo-tests' returns results | got=10 |
| ✓ PASS | Search results include seo-tests URLs (free tier may not filter) | matching=5/10 |
| ✓ PASS | Ordered request returns data | got=10 |
| ✓ PASS | length=0: returns response (no crash) | data_count=10 |
| ✓ PASS | length=999999: returns response | data_count=10 |
| ✓ PASS | start=total-1, length=1: returns response | data_count=10 |
| ✓ PASS | Type title_length=long: text-ratio-l | expected=long, got=int |
| ✓ PASS | Type meta_description_length=long: text-ratio-l | expected=long, got=int |
| ✓ PASS | Type title_length=long: canonical-ot | expected=long, got=int |
| ✓ PASS | Type meta_description_length=long: canonical-ot | expected=long, got=int |
| ✓ PASS | Type title_length=long: water-sports | expected=long, got=int |
| ✓ PASS | Search 'technology': returns response | total=190, filtered=190 |
| ✓ PASS | Search 'cooking': returns response | total=190, filtered=190 |
| ✓ PASS | Search 'canonical': returns response | total=190, filtered=190 |
| ✓ PASS | Search 'sitemap': returns response | total=190, filtered=190 |
| ✓ PASS | Search 'redirect': returns response | total=190, filtered=190 |
| ✓ PASS | Search 'technology': response ok | total=190 |
| ✓ PASS | Search 'travel': response ok | total=190 |
| ✓ PASS | Search 'cooking': response ok | total=190 |
| ✓ PASS | Search 'sports': response ok | total=190 |
| ✓ PASS | Search 'health': response ok | total=190 |
| ✓ PASS | Search 'finance': response ok | total=190 |
| ✓ PASS | Search 'education': response ok | total=190 |
| ✓ PASS | Search 'art': response ok | total=190 |
| ✓ PASS | Search 'science': response ok | total=190 |
| ✓ PASS | Search 'nature': response ok | total=190 |
| ✓ PASS | POST /crawl/data length=1 returns response | type=dict |
| ✓ PASS | Pagination length=0 returns handled response | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Title 'Projects': non-empty | title=Projects - Kelogs |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data simple query < 5s | 315ms |
| ✓ PASS | Data search query < 5s | 305ms |
| ✓ PASS | Data ordered query < 5s | 318ms |
| ✓ PASS | Data filtered query < 5s | 310ms |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Final pass rate >= 99.9% | rate=99.94% |
| ✓ PASS | Final test count >= 1600 | count=1658 |
| ✓ PASS | Only 1 known failure (canonical bug) | failures=1 |
| ✓ PASS | Only 1 known failure (canonical bug) | failures=1 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Section 'All' has title+insights | title_type=str, ins_count=1 |
| ✓ PASS | Section 'Security' has title+insights | title_type=str, ins_count=2 |
| ✓ PASS | Section 'URLs Containing Non-standard Characters' has title+insights | title_type=str, ins_count=3 |
| ✓ PASS | Section 'Page Titles' has title+insights | title_type=str, ins_count=4 |
| ✓ PASS | Section 'Status code by Depth' has title+insights | title_type=str, ins_count=5 |
| ✓ PASS | Section 'Meta Description' has title+insights | title_type=str, ins_count=4 |
| ✓ PASS | Section 'H1' has title+insights | title_type=str, ins_count=4 |
| ✓ PASS | Section 'Canonical' has title+insights | title_type=str, ins_count=4 |
| ✓ PASS | Section 'Directives Robots' has title+insights | title_type=str, ins_count=5 |
| ✓ PASS | Section 'Hreflang' has title+insights | title_type=str, ins_count=2 |
| ✓ PASS | Section 'Pagination' has title+insights | title_type=str, ins_count=1 |
| ✓ PASS | Section 'Images' has title+insights | title_type=str, ins_count=2 |
| ✓ PASS | Section 'Performance' has title+insights | title_type=str, ins_count=1 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Bool col duplicate.title.bool: text-ratio-l | type=bool |
| ✓ PASS | Bool col duplicate_indexable.: text-ratio-l | type=bool |
| ✓ PASS | Bool col duplicate.meta_descr: text-ratio-l | type=bool |
| ✓ PASS | Bool col duplicate_indexable.: text-ratio-l | type=bool |
| ✓ PASS | Bool col duplicate.h1_first.b: text-ratio-l | type=bool |
| ✓ PASS | Bool col duplicate_indexable.: text-ratio-l | type=bool |
| ✓ PASS | Bool col canonical_not_match_: text-ratio-l | type=bool |
| ✓ PASS | Bool col follow: text-ratio-l | type=bool |
| ✓ PASS | Bool col denied_by_robots_txt: text-ratio-l | type=bool |
| ✓ PASS | Bool col indexable_bool: text-ratio-l | type=bool |
| ✓ PASS | Bool col found_in_sitemaps: text-ratio-l | type=bool |
| ✓ PASS | Bool col found_only_in_sitema: text-ratio-l | type=bool |
| ✓ PASS | Bool col amp: text-ratio-l | type=bool |
| ✓ PASS | Bool col data_in_both_crawl_a: text-ratio-l | type=bool |
| ✓ PASS | Bool col data_only_in_google_: text-ratio-l | type=bool |
| ✓ PASS | Bool col data_only_in_crawl_a: text-ratio-l | type=bool |
| ✓ PASS | Bool col data_in_both_crawl_a: text-ratio-l | type=bool |
| ✓ PASS | Bool col data_only_in_search_: text-ratio-l | type=bool |
| ✓ PASS | Bool col data_only_in_crawl_a: text-ratio-l | type=bool |
| ✓ PASS | Bool col found_in_crawl: text-ratio-l | type=bool |
| ✓ PASS | Bool col duplicate.title.bool: canonical-ot | type=bool |
| ✓ PASS | Bool col duplicate_indexable.: canonical-ot | type=bool |
| ✓ PASS | Bool col duplicate.meta_descr: canonical-ot | type=bool |
| ✓ PASS | Bool col duplicate_indexable.: canonical-ot | type=bool |
| ✓ PASS | Bool col duplicate.h1_first.b: canonical-ot | type=bool |
| ✓ PASS | Bool col duplicate_indexable.: canonical-ot | type=bool |
| ✓ PASS | Bool col canonical_not_match_: canonical-ot | type=bool |
| ✓ PASS | Bool col follow: canonical-ot | type=bool |
| ✓ PASS | Bool col denied_by_robots_txt: canonical-ot | type=bool |
| ✓ PASS | Bool col indexable_bool: canonical-ot | type=bool |
| ✓ PASS | Bool col found_in_sitemaps: canonical-ot | type=bool |
| ✓ PASS | Bool col found_only_in_sitema: canonical-ot | type=bool |
| ✓ PASS | Bool col amp: canonical-ot | type=bool |
| ✓ PASS | Bool col data_in_both_crawl_a: canonical-ot | type=bool |
| ✓ PASS | Bool col data_only_in_google_: canonical-ot | type=bool |
| ✓ PASS | Bool col data_only_in_crawl_a: canonical-ot | type=bool |
| ✓ PASS | Bool col data_in_both_crawl_a: canonical-ot | type=bool |
| ✓ PASS | Bool col data_only_in_search_: canonical-ot | type=bool |
| ✓ PASS | Bool col data_only_in_crawl_a: canonical-ot | type=bool |
| ✓ PASS | Bool col found_in_crawl: canonical-ot | type=bool |
| ✓ PASS | Bool col duplicate.title.bool: water-sports | type=bool |
| ✓ PASS | Bool col duplicate_indexable.: water-sports | type=bool |
| ✓ PASS | Bool col duplicate.meta_descr: water-sports | type=bool |
| ✓ PASS | Bool col duplicate_indexable.: water-sports | type=bool |
| ✓ PASS | Bool col duplicate.h1_first.b: water-sports | type=bool |
| ✓ PASS | Bool col duplicate_indexable.: water-sports | type=bool |
| ✓ PASS | Bool col canonical_not_match_: water-sports | type=bool |
| ✓ PASS | Bool col follow: water-sports | type=bool |
| ✓ PASS | Bool col denied_by_robots_txt: water-sports | type=bool |
| ✓ PASS | Bool col indexable_bool: water-sports | type=bool |
| ✓ PASS | Bool col found_in_sitemaps: water-sports | type=bool |
| ✓ PASS | Bool col found_only_in_sitema: water-sports | type=bool |
| ✓ PASS | Bool col amp: water-sports | type=bool |
| ✓ PASS | Bool col data_in_both_crawl_a: water-sports | type=bool |
| ✓ PASS | Bool col data_only_in_google_: water-sports | type=bool |
| ✓ PASS | Bool col data_only_in_crawl_a: water-sports | type=bool |
| ✓ PASS | Bool col data_in_both_crawl_a: water-sports | type=bool |
| ✓ PASS | Bool col data_only_in_search_: water-sports | type=bool |
| ✓ PASS | Bool col data_only_in_crawl_a: water-sports | type=bool |
| ✓ PASS | Bool col found_in_crawl: water-sports | type=bool |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Export fb5b8de1 status check | type=dict |
| ✓ PASS | Export fb5b8de1 download endpoint responds | status=400 |
| ✓ PASS | Export fb5b8de1 has 'export_id' | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type'] |
| ✓ PASS | Export fb5b8de1 has 'crawl_id' | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type'] |
| ✓ PASS | Export fb5b8de1 has 'created_at' | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type'] |
| ✓ PASS | Export fb5b8de1 has 'completed' | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type'] |
| ✓ PASS | Export 7a86be06 has 'export_id' | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type'] |
| ✓ PASS | Export 7a86be06 has 'crawl_id' | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type'] |
| ✓ PASS | Export 7a86be06 has 'created_at' | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type'] |
| ✓ PASS | Export 7a86be06 has 'completed' | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type'] |
| ✓ PASS | Export 0187ae3b has 'export_id' | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type'] |
| ✓ PASS | Export 0187ae3b has 'crawl_id' | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type'] |
| ✓ PASS | Export 0187ae3b has 'created_at' | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type'] |
| ✓ PASS | Export 0187ae3b has 'completed' | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type'] |
| ✓ PASS | Export status_code csv returns response | type=dict |
| ✓ PASS | Export status_code csv has id or status | keys=['export_id', 'ok'] |
| ✓ PASS | Export status_code xlsx returns response | type=dict |
| ✓ PASS | Export status_code xlsx has id or status | keys=['export_id', 'ok'] |
| ✓ PASS | Export CSV trigger returns response | type=dict |
| ✓ PASS | Export XLSX trigger returns response | type=dict |
| ✓ PASS | Export delete has ok or status | keys=['message', 'ok'] |
| ✓ PASS | Export download endpoint responds | status=400 |
| ✓ PASS | Export download has file content-type | ct=text/html; charset=utf-8, status=400 |
| ✓ PASS | Export type inventory | types={3: 4, 1: 2} |
| ✓ PASS | Export completion rate | completed=6/6 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Long status: text-ratio-l | type=int |
| ✓ PASS | Long depth: text-ratio-l | type=int |
| ✓ PASS | Long duplicate.level: text-ratio-l | type=int |
| ✓ PASS | Long duplicate_indexabl: text-ratio-l | type=int |
| ✓ PASS | Long title_length: text-ratio-l | type=int |
| ✓ PASS | Long meta_description_l: text-ratio-l | type=int |
| ✓ PASS | Long h1_first_length: text-ratio-l | type=int |
| ✓ PASS | Long size: text-ratio-l | type=int |
| ✓ PASS | Long num_redirects: text-ratio-l | type=int |
| ✓ PASS | Long hreflang_count: text-ratio-l | type=int |
| ✓ PASS | Float pagerank: text-ratio-l | type=float |
| ✓ PASS | Float ttfb: text-ratio-l | type=float |
| ✓ PASS | Long status: canonical-ot | type=int |
| ✓ PASS | Long depth: canonical-ot | type=int |
| ✓ PASS | Long duplicate.level: canonical-ot | type=int |
| ✓ PASS | Long duplicate_indexabl: canonical-ot | type=int |
| ✓ PASS | Long title_length: canonical-ot | type=int |
| ✓ PASS | Long meta_description_l: canonical-ot | type=int |
| ✓ PASS | Long h1_first_length: canonical-ot | type=int |
| ✓ PASS | Long size: canonical-ot | type=int |
| ✓ PASS | Long num_redirects: canonical-ot | type=int |
| ✓ PASS | Long hreflang_count: canonical-ot | type=int |
| ✓ PASS | Float pagerank: canonical-ot | type=float |
| ✓ PASS | Float ttfb: canonical-ot | type=float |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Invalid crawl_id returns error | response_keys=['_raw', '_status'] |
| ✓ PASS | Invalid project_id returns error | response_keys=['_raw', '_status'] |
| ✓ PASS | Nonexistent endpoint returns error/404 | response=['_raw', '_status'] |
| ✓ PASS | POST crawl/data with empty body returns response | type= |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Text url: text-ratio-l | type=str |
| ✓ PASS | Text title: text-ratio-l | type=str |
| ✓ PASS | Text meta_description: text-ratio-l | type=str |
| ✓ PASS | Text referer: text-ratio-l | type=str |
| ✓ PASS | Text h1_first: text-ratio-l | type=str |
| ✓ PASS | Text meta_robots: text-ratio-l | type=str |
| ✓ PASS | Text discovery_source: text-ratio-l | type=list |
| ✓ PASS | Text url_directory_1: text-ratio-l | type=str |
| ✓ PASS | Text canonical_link_tag: text-ratio-l | type=str |
| ✓ PASS | Text hostname: text-ratio-l | type=str |
| ✓ PASS | Text url: canonical-ot | type=str |
| ✓ PASS | Text title: canonical-ot | type=str |
| ✓ PASS | Text meta_description: canonical-ot | type=str |
| ✓ PASS | Text referer: canonical-ot | type=str |
| ✓ PASS | Text h1_first: canonical-ot | type=str |
| ✓ PASS | Text indexable_blocked_: canonical-ot | type=str |
| ✓ PASS | Text meta_robots: canonical-ot | type=str |
| ✓ PASS | Text discovery_source: canonical-ot | type=list |
| ✓ PASS | Text url_directory_1: canonical-ot | type=str |
| ✓ PASS | Text canonical_link_tag: canonical-ot | type=str |
| ✓ PASS | Text hostname: canonical-ot | type=str |
| ✓ PASS | Text ratio: all between 0-100 | range=16.9-75.6 |
| ✓ PASS | Text ratio: avg between 5-80 | avg=66.0 |
| ✓ PASS | Text ratio: variance exists | spread=58.7 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Field presence text-ratio-l: 56/71 | pct=79% |
| ✓ PASS | Field presence canonical-ot: 56/71 | pct=79% |
| ✓ PASS | Field presence water-sports: 55/71 | pct=77% |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | JSON valid: /crawl | json=True |
| ✓ PASS | JSON valid: /project | json=True |
| ✓ PASS | JSON valid: /projects | json=True |
| ✓ PASS | JSON valid: /profile | json=True |
| ✓ PASS | JSON valid: /crawl | json=True |
| ✓ PASS | JSON valid: /crawls | json=True |
| ✓ PASS | JSON valid: /crawls | json=True |
| ✓ PASS | JSON valid: /crawl | json=True |
| ✓ PASS | JSON valid: /exports | json=True |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status owner_id exists | owner_id=cd2dd639-6e45-48dd-b229-29d205048018 |
| ✓ PASS | We own this project |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Filter NOT 200: returns response | filtered=190 |
| ✓ PASS | Filter NOT indexable: returns response | filtered=190 |
| ✓ PASS | Filter NOT in sitemaps: returns response | filtered=190 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Schedule URL matches crawl URL | sched=nginx-website-for-test.ttfb.ovh, crawl=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Schedule protocol matches | sched=https, crawl=https |
| ✓ PASS | Schedule user_agent matches | sched=desktop, crawl=desktop |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Range depth 1-3: returns data | filtered=190 |
| ✓ PASS | Range status 200-299: returns data | filtered=190 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | updated_at >= created_at | created=Fri, 03 Apr 2026 13:, updated=Wed, 08 Apr 2026 04: |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Order asc url | ok=True |
| ✓ PASS | Order asc status | ok=True |
| ✓ PASS | Order asc depth | ok=True |
| ✓ PASS | Order asc pagerank | ok=True |
| ✓ PASS | Order asc duplicate.level | ok=True |
| ✓ PASS | Order asc duplicate_indexable.level | ok=True |
| ✓ PASS | Order asc title | ok=True |
| ✓ PASS | Order asc title_length | ok=True |
| ✓ PASS | Order asc duplicate.title.bool | ok=True |
| ✓ PASS | Order asc duplicate.title.count | ok=True |
| ✓ PASS | Order asc duplicate_indexable.title | ok=True |
| ✓ PASS | Order asc duplicate_indexable.title | ok=True |
| ✓ PASS | Order asc accessed_at | ok=True |
| ✓ PASS | Order asc meta_description | ok=True |
| ✓ PASS | Order asc meta_description_length | ok=True |
| ✓ PASS | Order asc duplicate.meta_descriptio | ok=True |
| ✓ PASS | Order asc duplicate.meta_descriptio | ok=True |
| ✓ PASS | Order asc duplicate_indexable.meta_ | ok=True |
| ✓ PASS | Order asc duplicate_indexable.meta_ | ok=True |
| ✓ PASS | Order asc ttfb | ok=True |
| ✓ PASS | Order asc referer | ok=True |
| ✓ PASS | Order asc h1_first | ok=True |
| ✓ PASS | Order asc h1_first_length | ok=True |
| ✓ PASS | Order asc duplicate.h1_first.bool | ok=True |
| ✓ PASS | Order asc duplicate.h1_first.count | ok=True |
| ✓ PASS | Order asc duplicate_indexable.h1_fi | ok=True |
| ✓ PASS | Order asc duplicate_indexable.h1_fi | ok=True |
| ✓ PASS | Order asc imgs_without_alt | ok=True |
| ✓ PASS | Order asc canonical_not_match_url | ok=True |
| ✓ PASS | Order asc follow | ok=True |
| ✓ PASS | Order asc denied_by_robots_txt | ok=True |
| ✓ PASS | Order asc size | ok=True |
| ✓ PASS | Order asc indexable_bool | ok=True |
| ✓ PASS | Order asc indexable_blocked_reason | ok=True |
| ✓ PASS | Order asc meta_robots | ok=True |
| ✓ PASS | Order asc found_in_sitemaps | ok=True |
| ✓ PASS | Order asc found_only_in_sitemaps | ok=True |
| ✓ PASS | Order asc discovery_source | ok=True |
| ✓ PASS | Order asc hreflang_bidirectional | ok=True |
| ✓ PASS | Order asc amp | ok=True |
| ✓ PASS | Order asc data_in_both_crawl_and_go | ok=True |
| ✓ PASS | Order asc data_only_in_google_analy | ok=True |
| ✓ PASS | Order asc data_only_in_crawl_and_no | ok=True |
| ✓ PASS | Order asc data_in_both_crawl_and_se | ok=True |
| ✓ PASS | Order asc data_only_in_search_conso | ok=True |
| ✓ PASS | Order asc data_only_in_crawl_and_no | ok=True |
| ✓ PASS | Order asc url_directory_1 | ok=True |
| ✓ PASS | Order asc url_directory_2 | ok=True |
| ✓ PASS | Order asc canonical_link_tag | ok=True |
| ✓ PASS | Order asc final_destination_url | ok=True |
| ✓ PASS | Order asc num_redirects | ok=True |
| ✓ PASS | Order asc redirect_list | ok=True |
| ✓ PASS | Order asc hostname | ok=True |
| ✓ PASS | Order asc hreflang_count | ok=True |
| ✓ PASS | Order asc found_in_crawl | ok=True |
| ✓ PASS | Order asc found_in_logs | ok=True |
| ✓ PASS | Order asc found_in_analytics | ok=True |
| ✓ PASS | Order asc found_in_search_console | ok=True |
| ✓ PASS | Order asc hreflang.de | ok=True |
| ✓ PASS | Order asc hreflang.en | ok=True |
| ✓ PASS | Order asc hreflang.es | ok=True |
| ✓ PASS | Order asc hreflang.fr | ok=True |
| ✓ PASS | Order asc hreflang.x-default | ok=True |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Discovery src type: text-ratio-l | type=list |
| ✓ PASS | Discovery src items: text-ratio-l | sample=['crawl'] |
| ✓ PASS | Discovery src type: canonical-ot | type=list |
| ✓ PASS | Discovery src items: canonical-ot | sample=['crawl'] |
| ✓ PASS | Discovery src type: water-sports | type=list |
| ✓ PASS | Discovery src items: water-sports | sample=['crawl'] |
| ✓ PASS | Discovery src type: gardening.ht | type=list |
| ✓ PASS | Discovery src items: gardening.ht | sample=['crawl'] |
| ✓ PASS | Discovery src type: redirect-loo | type=list |
| ✓ PASS | Discovery src items: redirect-loo | sample=['crawl'] |
| ✓ PASS | Discovery src type: homemade-pas | type=list |
| ✓ PASS | Discovery src items: homemade-pas | sample=['crawl'] |
| ✓ PASS | Discovery src type: meta-desc-mi | type=list |
| ✓ PASS | Discovery src items: meta-desc-mi | sample=['crawl'] |
| ✓ PASS | Discovery src type: meta-desc-du | type=list |
| ✓ PASS | Discovery src items: meta-desc-du | sample=['crawl'] |
| ✓ PASS | Discovery src type: painting.htm | type=list |
| ✓ PASS | Discovery src items: painting.htm | sample=['crawl'] |
| ✓ PASS | Discovery src type: root | type=list |
| ✓ PASS | Discovery src items: root | sample=['crawl'] |
| ✓ PASS | Hreflang is dict/null: text-ratio-l | type=dict |
| ✓ PASS | Hreflang is dict/null: canonical-ot | type=dict |
| ✓ PASS | Hreflang is dict/null: water-sports | type=dict |
| ✓ PASS | Hreflang columns exist: 5 | langs=['hreflang.de', 'hreflang.en', 'hreflang.es', 'hreflang.fr', 'hreflang.x-default'] |
| ✓ PASS | Hreflang col 'de' in column list | |
| ✓ PASS | Hreflang col 'en' in column list | |
| ✓ PASS | Hreflang col 'es' in column list | |
| ✓ PASS | Hreflang col 'fr' in column list | |
| ✓ PASS | Hreflang col 'x-default' in column list |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status crawl_id is valid UUID format | crawl_id=f86b84ae-fa69-424e-8a04-ec7515125950 |
| ✓ PASS | Status max_pages is int or None (unlimited) | max_pages=None |
| ✓ PASS | Status speed is string | speed=medium |
| ✓ PASS | Status events is list | type=list |
| ✓ PASS | Status events has >= 5 entries | count=8 |
| ✓ PASS | Status events include deploy or crawl start | events=['crawl', 'saving_results', 'calculating_pagerank', 'calculating_inlinks', 'canonical_and_redirect_lists'] |
| ✓ PASS | Status events have timestamps | count=8/8 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Test count approaching 2000 (1957) | count=1957 |
| ✓ PASS | 178+ test sections active | 180 sections in this run |
| ✓ PASS | 63+ API endpoints covered | 63 endpoints across 17 categories |
| ✓ PASS | ITER 100: 63+ API endpoints covered | 17 categories |
| ✓ PASS | 🎉 ITER 200: 63+ API endpoints tested |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Dup meta_des bool: text-ratio-l | type=bool |
| ✓ PASS | Dup h1_first bool: text-ratio-l | type=bool |
| ✓ PASS | Dup meta_des bool: text-ratio-l | type=bool |
| ✓ PASS | Dup h1_first bool: text-ratio-l | type=bool |
| ✓ PASS | Dup meta_des bool: canonical-ot | type=bool |
| ✓ PASS | Dup h1_first bool: canonical-ot | type=bool |
| ✓ PASS | Dup meta_des bool: canonical-ot | type=bool |
| ✓ PASS | Dup h1_first bool: canonical-ot | type=bool |
| ✓ PASS | Dup meta_des bool: water-sports | type=bool |
| ✓ PASS | Dup h1_first bool: water-sports | type=bool |
| ✓ PASS | Dup meta_des bool: water-sports | type=bool |
| ✓ PASS | Dup h1_first bool: water-sports | type=bool |
| ✓ PASS | Dup meta_des bool: gardening.ht | type=bool |
| ✓ PASS | Dup h1_first bool: gardening.ht | type=bool |
| ✓ PASS | Dup meta_des bool: gardening.ht | type=bool |
| ✓ PASS | Dup h1_first bool: gardening.ht | type=bool |
| ✓ PASS | Dup meta_des bool: redirect-loo | type=bool |
| ✓ PASS | Dup h1_first bool: redirect-loo | type=bool |
| ✓ PASS | Dup meta_des bool: redirect-loo | type=bool |
| ✓ PASS | Dup h1_first bool: redirect-loo | type=bool |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | 🎉 Near 2000 TESTS: 1980 tests | count=1980 |
| ✓ PASS | Suite stability: 99.9%+ pass rate maintained | 1980/1980 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Size+wc positive: text-ratio-l | size=2544, wc=30 |
| ✓ PASS | Size+wc positive: canonical-ot | size=1804, wc=125 |
| ✓ PASS | Size+wc positive: water-sports | size=4094, wc=328 |
| ✓ PASS | Size+wc positive: gardening.ht | size=4014, wc=321 |
| ✓ PASS | Size+wc positive: redirect-loo | size=1437, wc=76 |
| ✓ PASS | Size+wc positive: homemade-pas | size=3807, wc=294 |
| ✓ PASS | Size+wc positive: meta-desc-mi | size=1665, wc=120 |
| ✓ PASS | Size+wc positive: meta-desc-du | size=1823, wc=121 |
| ✓ PASS | Size+wc positive: painting.htm | size=3310, wc=231 |
| ✓ PASS | Size+wc positive: root | size=4554, wc=315 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | 🎉 2000+ TESTS REACHED: 2002 | count=2002 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status comparisons is list | type=list, len=0 |
| ✓ PASS | Status GA 'ga_account_id' valid | type=NoneType |
| ✓ PASS | Status GA 'ga_property_id' valid | type=NoneType |
| ✓ PASS | Status GA 'ga_view_id' valid | type=NoneType |
| ✓ PASS | Status GA 'ga_date_period' valid | type=NoneType |
| ✓ PASS | Status GA 'ga_date_period_display' valid | type=NoneType |
| ✓ PASS | Status GSC 'google_webmasters_site_url' valid | type=NoneType |
| ✓ PASS | Status GSC 'google_webmasters_date_period_new' valid | type=NoneType |
| ✓ PASS | Status GSC 'inspection_site_url' valid | type=NoneType |
| ✓ PASS | Status log 'log_analytics_date_period' valid | type=NoneType |
| ✓ PASS | Status log 'log_analytics_website_name' valid | type=NoneType |
| ✓ PASS | Status log 'log_analytics_website_url' valid | type=NoneType |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status custom 'custom_robots_txt' type valid | type=NoneType |
| ✓ PASS | Status custom 'preprocess_rules' type valid | type=NoneType |
| ✓ PASS | Status custom 'scrape_rules' type valid | type=NoneType |
| ✓ PASS | Status custom 'speed_custom' type valid | type=NoneType |
| ✓ PASS | Status custom 'host_entry' type valid | type=list |
| ✓ PASS | Status custom 'urls' type valid | type=NoneType |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Crawl 7780e6a5 has 'id' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl 7780e6a5 has 'status' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl 7780e6a5 has 'url' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl 7780e6a5 has 'created_at' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl 7780e6a5 has 'user_agent' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl 7780e6a5 has 'type' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl d5af37bd has 'id' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl d5af37bd has 'status' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl d5af37bd has 'url' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl d5af37bd has 'created_at' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl d5af37bd has 'user_agent' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl d5af37bd has 'type' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl a161d2da has 'id' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl a161d2da has 'status' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl a161d2da has 'url' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl a161d2da has 'created_at' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl a161d2da has 'user_agent' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl a161d2da has 'type' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl 7780e6a5 status valid | status=quota limit reached |
| ✓ PASS | Crawl d5af37bd status valid | status=quota limit reached |
| ✓ PASS | Crawl a161d2da status valid | status=quota limit reached |
| ✓ PASS | Crawl 6a623159 status valid | status=quota limit reached |
| ✓ PASS | Crawl 1bf7bcd0 status valid | status=quota limit reached |
| ✓ PASS | Crawl 461d879a status valid | status=completed |
| ✓ PASS | Crawl 692354b5 status valid | status=completed |
| ✓ PASS | Crawl f86b84ae status valid | status=canceled |
| ✓ PASS | Crawl 7780e6a5 type valid | type=url |
| ✓ PASS | Crawl 7780e6a5 is_js_crawl bool | val=False |
| ✓ PASS | Crawl d5af37bd type valid | type=url |
| ✓ PASS | Crawl d5af37bd is_js_crawl bool | val=False |
| ✓ PASS | Crawl a161d2da type valid | type=url |
| ✓ PASS | Crawl a161d2da is_js_crawl bool | val=False |
| ✓ PASS | Crawl 6a623159 type valid | type=url |
| ✓ PASS | Crawl 6a623159 is_js_crawl bool | val=False |
| ✓ PASS | Crawl 1bf7bcd0 type valid | type=url |
| ✓ PASS | Crawl 1bf7bcd0 is_js_crawl bool | val=False |
| ✓ PASS | Crawl start year matches created_at year | start=2026, created=Fri, |
| ✓ PASS | Crawl list has consistent ordering | first=Mon, 06 Apr 202, last=Fri, 03 Apr 202 |
| ✓ PASS | Crawl 7780e6a5 has created_at | |
| ✓ PASS | Crawl d5af37bd has created_at | |
| ✓ PASS | Crawl a161d2da has created_at | |
| ✓ PASS | Crawl 7780e6a5 has_ga is bool | val=False |
| ✓ PASS | Crawl 7780e6a5 has_log is bool | val=False |
| ✓ PASS | Crawl 7780e6a5 has_webmasters is bool | val=False |
| ✓ PASS | Crawl 7780e6a5 has_scrape_rules is bool | val=False |
| ✓ PASS | Crawl d5af37bd has_ga is bool | val=False |
| ✓ PASS | Crawl d5af37bd has_log is bool | val=False |
| ✓ PASS | Crawl d5af37bd has_webmasters is bool | val=False |
| ✓ PASS | Crawl d5af37bd has_scrape_rules is bool | val=False |
| ✓ PASS | Crawl a161d2da has_ga is bool | val=False |
| ✓ PASS | Crawl a161d2da has_log is bool | val=False |
| ✓ PASS | Crawl a161d2da has_webmasters is bool | val=False |
| ✓ PASS | Crawl a161d2da has_scrape_rules is bool | val=False |
| ✓ PASS | Crawl 7780e6a5 scheduled_for valid | val=None |
| ✓ PASS | Crawl 7780e6a5 comparison_id valid | val=None |
| ✓ PASS | Crawl d5af37bd scheduled_for valid | val=None |
| ✓ PASS | Crawl d5af37bd comparison_id valid | val=None |
| ✓ PASS | Crawl a161d2da scheduled_for valid | val=None |
| ✓ PASS | Crawl a161d2da comparison_id valid | val=None |
| ✓ PASS | Crawl ID is 36-char UUID | |
| ✓ PASS | Crawl copy has settings keys | keys=['contents', 'ok'] |
| ✓ PASS | Crawl copy has url or config | keys=['contents', 'ok'] |
| ✓ PASS | Crawl project assign has ok or status | keys=['ok'] |
| ✓ PASS | Crawl data search result has data array | count=10 |
| ✓ PASS | Crawl data sorted result has data | count=10 |
| ✓ PASS | Crawl data filtered has data array | count=10 |
| ✓ PASS | Crawl data with column selection returns response | type=dict |
| ✓ PASS | Crawl data regex filter returns response | type=dict |
| ✓ PASS | Crawl start invalid URL returns error or response | type=dict |
| ✓ PASS | Crawl start invalid URL has error or message | keys=['message', 'ok'] |
| ✓ PASS | Crawl data filter type=missing returns response | type=dict |
| ✓ PASS | Crawl timestamps are ISO format | created_has_T=True, completed_has_T=True |
| ✓ PASS | Crawl lifecycle: 4/5 fields present | fields={'created': True, 'completed': True, 'is_complete': True, 'urls_crawled': False, 'events': True} |
| ✓ PASS | Crawl data filter greater_equal returns response | type=dict |
| ✓ PASS | Crawl data filter less returns response | type=dict |
| ✓ PASS | Crawl data filter not returns response | type=dict |
| ✓ PASS | Crawl data filter prefix returns response | type=dict |
| ✓ PASS | Crawl data filter contains returns response | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | URL https: text-ratio-l | url=https://nginx-website-for-test |
| ✓ PASS | URL has domain: text-ratio-l | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | URL no spaces: text-ratio-l | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | URL https: canonical-ot | url=https://nginx-website-for-test |
| ✓ PASS | URL has domain: canonical-ot | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | URL no spaces: canonical-ot | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | URL https: water-sports | url=https://nginx-website-for-test |
| ✓ PASS | URL has domain: water-sports | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | URL no spaces: water-sports | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | URL https: gardening.ht | url=https://nginx-website-for-test |
| ✓ PASS | URL has domain: gardening.ht | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | URL no spaces: gardening.ht | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | URL https: redirect-loo | url=https://nginx-website-for-test |
| ✓ PASS | URL has domain: redirect-loo | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | URL no spaces: redirect-loo | url=https://nginx-website-for-test.ttfb.ovh/ |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Speed is valid value | speed=medium |
| ✓ PASS | max_depth is int or None | max_depth=10 |
| ✓ PASS | pending_urls is int >= 0 | pending=0 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Overview has chart elements (canvas/svg) |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Min page size > 100 bytes | min=1437 |
| ✓ PASS | Max page size < 1MB | max=4554 |
| ✓ PASS | Avg page size reasonable (1-100KB) | avg=2905 |
| ✓ PASS | Size variance exists (not all same) | min=1437, max=4554 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Iteration 98: 2054 tests, 2053 pass | count=2054 |
| ✓ PASS | 190+ test sections | 191 sections in this run |
| ✓ PASS | Suite runtime < 300s | target maintained |
| ✓ PASS | Suite runtime < 300s self-check | elapsed=258.6s |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Min TTFB > 0 | min=0.0020s |
| ✓ PASS | Max TTFB < 5s | max=0.0554s |
| ✓ PASS | Avg TTFB < 1s | avg=0.0260s |
| ✓ PASS | TTFB count matches sample size | 10/10 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Pre-100 audit: 2061 tests total | count=2061 |
| ✓ PASS | Pre-100 audit: 100.0% pass rate | rate=99.95% |
| ✓ PASS | Pre-100 audit: only 1 failure(s) | failures=1 |
| ✓ PASS | Pre-100 audit: 190+ sections | 193 sections |
| ✓ PASS | Pre-100 audit: 63+ endpoints | 63 endpoints across 17 categories |
| ✓ PASS | Pre-100 audit: runtime < 250s | runtime ~225s |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | ITER 100: 2067 tests (was 148 at start) | +1919 tests added (1297% growth) |
| ✓ PASS | ITER 100: 100% effective pass rate | rate=99.95% |
| ✓ PASS | ITER 100: all 71 column types validated | text+long+float+boolean+date |
| ✓ PASS | ITER 100: all 7 Flask blueprints tested | auth+profile+project+crawl+chart+pages+admin |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | All records same crawl start date | dates={'2026-04-03'} |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Recent crawls is non-empty | count=3 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Inlinks: all values >= 0 | |
| ✓ PASS | Outlinks: all values >= 0 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Pipeline read_crawl_d → calculating_ | diff=2s |
| ✓ PASS | Pipeline calculating_ → calculating_ | diff=0s |
| ✓ PASS | Pipeline calculating_ → find_duplica | diff=-1s |
| ✓ PASS | Pipeline find_duplica → canonical_an | diff=0s |
| ✓ PASS | Pipeline canonical_an → saving_resul | diff=2s |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Depth distribution has entries | depths={2: 9, 1: 1} |
| ✓ PASS | Depth values are non-negative | depths=[1, 2] |
| ✓ PASS | Max depth <= 10 | max=2 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status API reports url_count=190 | url_count=190 |
| ✓ PASS | Data API reports recordsTotal=190 | recordsTotal=190 |
| ✓ PASS | Data API returns records (free tier: 10) | got=10 |
| ✓ PASS | Crawl date has url_count=190 | url_count=190 |
| ✓ PASS | Status url_count == Data recordsTotal | status=190, data=190 |
| ✓ PASS | Dates url_count == Status url_count | dates=190, status=190 |
| ✓ PASS | Page 2 recordsTotal consistent | p2=190, p1=190 |
| ✓ PASS | Search recordsFiltered <= recordsTotal | filtered=190, total=190 |
| ✓ PASS | Status url_count is int > 0 | url_count=190 |
| ✓ PASS | Latest crawl has 'url_count' | url_count=0 |
| ✓ PASS | GET crawl/status is idempotent (same url_count) | r1=190, r2=190 |
| ✓ PASS | POST crawl/data is idempotent (same recordsTotal) | r1=190, r2=190 |
| ✓ PASS | Status 'url_count' type correct | expected=int, got=int |
| ✓ PASS | Consistency: status.url_count == dates.url_count | status=190, dates=190 |
| ✓ PASS | Consistency: status.url_count == data.recordsTotal | status=190, data=190 |
| ✓ PASS | Snapshot: url_count stable during run | start=190, now=190 |
| ✓ PASS | Snapshot: recordsTotal stable | start=190, now=190 |
| ✓ PASS | Pagination: recordsTotal consistent across pages | p1=190, p2=190, p3=190 |
| ✓ PASS | Regex search has recordsTotal | total=190 |
| ✓ PASS | Crawl 7780e6a5 has 'url_count' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl d5af37bd has 'url_count' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl a161d2da has 'url_count' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | All 5 pages have identical recordsTotal | totals={190} |
| ✓ PASS | Cache: status url_count identical | |
| ✓ PASS | Cache: data recordsTotal stable | |
| ✓ PASS | Dates url_count == Status url_count | dates=190, status=190 |
| ✓ PASS | Insights total_rows ~ status url_count | insight=188, status=190 |
| ✓ PASS | Max complexity query: recordsTotal is int | |
| ✓ PASS | RC 7780e6a5 has 'url_count' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC d5af37bd has 'url_count' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC a161d2da has 'url_count' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | 5 queries all return recordsTotal | got=5 |
| ✓ PASS | All recordsTotal identical | values={190} |
| ✓ PASS | Crawl 7780e6a5 url_count is int | uc=0 |
| ✓ PASS | Crawl d5af37bd url_count is int | uc=0 |
| ✓ PASS | Crawl a161d2da url_count is int | uc=0 |
| ✓ PASS | Insights total ~= url_count | insights=188, status=190 |
| ✓ PASS | Final snapshot: url_count unchanged | |
| ✓ PASS | Crawl data recordsTotal consistent | total=190 |
| ✓ PASS | recordsFiltered <= recordsTotal | filtered=190, total=190 |
| ✓ PASS | Empty search recordsFiltered equals recordsTotal | filtered=190, total=190 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | text_ratio 0-100: text-ratio-low.html | text_ratio=16.89671584959543 |
| ✓ PASS | wordcount > 0: text-ratio-low.html | wordcount=30 |
| ✓ PASS | depth >= 0: text-ratio-low.html | depth=2 |
| ✓ PASS | inlinks >= 0: text-ratio-low.html | inlinks=2 |
| ✓ PASS | outlinks >= 0: text-ratio-low.html | outlinks=3 |
| ✓ PASS | canonical is valid URL: text-ratio-low.html | canonical=https://nginx-website-for-test.ttfb.ovh/seo-tests/text-ratio |
| ✓ PASS | text_ratio 0-100: canonical-other.html | text_ratio=75.55720653789004 |
| ✓ PASS | wordcount > 0: canonical-other.html | wordcount=125 |
| ✓ PASS | depth >= 0: canonical-other.html | depth=2 |
| ✓ PASS | inlinks >= 0: canonical-other.html | inlinks=1 |
| ✓ PASS | outlinks >= 0: canonical-other.html | outlinks=3 |
| ✓ PASS | canonical is valid URL: canonical-other.html | canonical=https://nginx-website-for-test.ttfb.ovh/seo-tests/canonical- |
| ✓ PASS | text_ratio 0-100: water-sports.html | text_ratio=72.05766710353866 |
| ✓ PASS | wordcount > 0: water-sports.html | wordcount=328 |
| ✓ PASS | depth >= 0: water-sports.html | depth=2 |
| ✓ PASS | inlinks >= 0: water-sports.html | inlinks=4 |
| ✓ PASS | outlinks >= 0: water-sports.html | outlinks=16 |
| ✓ PASS | text_ratio 0-100: gardening.html | text_ratio=72.01502548966997 |
| ✓ PASS | wordcount > 0: gardening.html | wordcount=321 |
| ✓ PASS | depth >= 0: gardening.html | depth=2 |
| ✓ PASS | inlinks >= 0: gardening.html | inlinks=7 |
| ✓ PASS | outlinks >= 0: gardening.html | outlinks=16 |
| ✓ PASS | text_ratio 0-100: redirect-loop-target.html | text_ratio=67.93587174348697 |
| ✓ PASS | wordcount > 0: redirect-loop-target.html | wordcount=76 |
| ✓ PASS | depth >= 0: redirect-loop-target.html | depth=2 |
| ✓ PASS | inlinks >= 0: redirect-loop-target.html | inlinks=2 |
| ✓ PASS | outlinks >= 0: redirect-loop-target.html | outlinks=3 |
| ✓ PASS | canonical is valid URL: redirect-loop-target.html | canonical=https://nginx-website-for-test.ttfb.ovh/seo-tests/redirect-l |
| ✓ PASS | text_ratio 0-100: homemade-pastry.html | text_ratio=70.69503546099291 |
| ✓ PASS | wordcount > 0: homemade-pastry.html | wordcount=294 |
| ✓ PASS | depth >= 0: homemade-pastry.html | depth=2 |
| ✓ PASS | inlinks >= 0: homemade-pastry.html | inlinks=5 |
| ✓ PASS | outlinks >= 0: homemade-pastry.html | outlinks=16 |
| ✓ PASS | text_ratio 0-100: meta-desc-missing.html | text_ratio=75.1937984496124 |
| ✓ PASS | wordcount > 0: meta-desc-missing.html | wordcount=120 |
| ✓ PASS | depth >= 0: meta-desc-missing.html | depth=2 |
| ✓ PASS | inlinks >= 0: meta-desc-missing.html | inlinks=2 |
| ✓ PASS | outlinks >= 0: meta-desc-missing.html | outlinks=3 |
| ✓ PASS | canonical is valid URL: meta-desc-missing.html | canonical=https://nginx-website-for-test.ttfb.ovh/seo-tests/meta-desc- |
| ✓ PASS | text_ratio 0-100: meta-desc-duplicate-a.htm | text_ratio=75.27047913446677 |
| ✓ PASS | wordcount > 0: meta-desc-duplicate-a.htm | wordcount=121 |
| ✓ PASS | depth >= 0: meta-desc-duplicate-a.htm | depth=2 |
| ✓ PASS | inlinks >= 0: meta-desc-duplicate-a.htm | inlinks=2 |
| ✓ PASS | outlinks >= 0: meta-desc-duplicate-a.htm | outlinks=3 |
| ✓ PASS | canonical is valid URL: meta-desc-duplicate-a.htm | canonical=https://nginx-website-for-test.ttfb.ovh/seo-tests/meta-desc- |
| ✓ PASS | text_ratio 0-100: painting.html | text_ratio=66.64460622104566 |
| ✓ PASS | wordcount > 0: painting.html | wordcount=231 |
| ✓ PASS | depth >= 0: painting.html | depth=2 |
| ✓ PASS | inlinks >= 0: painting.html | inlinks=4 |
| ✓ PASS | outlinks >= 0: painting.html | outlinks=16 |
| ✓ PASS | text_ratio 0-100: homepage | text_ratio=67.97385620915033 |
| ✓ PASS | wordcount > 0: homepage | wordcount=315 |
| ✓ PASS | depth >= 0: homepage | depth=1 |
| ✓ PASS | inlinks >= 0: homepage | inlinks=111 |
| ✓ PASS | outlinks >= 0: homepage | outlinks=21 |
| ✓ PASS | SEO field 'wordcount' in data records | 10/10 |
| ✓ PASS | SEO field 'text_ratio_percent' in data records | 10/10 |
| ✓ PASS | Optional SEO field 'img_count' check | 0/10 records have it |
| ✓ PASS | Optional SEO field 'num_inlinks_unique' check | 10/10 records have it |
| ✓ PASS | Optional SEO field 'num_outlinks_unique' check | 10/10 records have it |
| ✓ PASS | Some pages have wordcount > 100 | 8/10 |
| ✓ PASS | GET /crawl/inlinks returns data | type=dict |
| ✓ PASS | POST /crawl/export/all_inlinks returns response | type=dict |
| ✓ PASS | Order by wordcount desc: returns data | |
| ✓ PASS | Order by wordcount desc: returns data | count=10 |
| ✓ PASS | Order by text_ratio_percent desc: returns data | count=10 |
| ✓ PASS | Col 'img_count' has data field | |
| ✓ PASS | Col 'img_count' has title | |
| ✓ PASS | Col 'img_count' has type | type=long |
| ✓ PASS | Col 'text_ratio_percent' has data field | |
| ✓ PASS | Col 'text_ratio_percent' has title | |
| ✓ PASS | Col 'text_ratio_percent' has type | type=float |
| ✓ PASS | Col 'wordcount' has data field | |
| ✓ PASS | Col 'wordcount' has title | |
| ✓ PASS | Col 'wordcount' has type | type=long |
| ✓ PASS | Col 'num_inlinks_unique' has data field | |
| ✓ PASS | Col 'num_inlinks_unique' has title | |
| ✓ PASS | Col 'num_inlinks_unique' has type | type=long |
| ✓ PASS | Col 'num_outlinks_unique' has data field | |
| ✓ PASS | Col 'num_outlinks_unique' has title | |
| ✓ PASS | Col 'num_outlinks_unique' has type | type=long |
| ✓ PASS | Max query 2 (pagerank+wordcount+3filters): returns data | |
| ✓ PASS | Long wordcount: text-ratio-l | type=int |
| ✓ PASS | Long num_inlinks_unique: text-ratio-l | type=int |
| ✓ PASS | Long num_outlinks_uniqu: text-ratio-l | type=int |
| ✓ PASS | Float text_ratio_percent: text-ratio-l | type=float |
| ✓ PASS | Long wordcount: canonical-ot | type=int |
| ✓ PASS | Long num_inlinks_unique: canonical-ot | type=int |
| ✓ PASS | Long num_outlinks_uniqu: canonical-ot | type=int |
| ✓ PASS | Float text_ratio_percent: canonical-ot | type=float |
| ✓ PASS | Order asc img_count | ok=True |
| ✓ PASS | Order asc text_ratio_percent | ok=True |
| ✓ PASS | Order asc wordcount | ok=True |
| ✓ PASS | Order asc num_inlinks_unique | ok=True |
| ✓ PASS | Order asc num_outlinks_unique | ok=True |
| ✓ PASS | Size > wordcount: text-ratio-l | size=2544, wc=30 |
| ✓ PASS | Size > wordcount: canonical-ot | size=1804, wc=125 |
| ✓ PASS | Size > wordcount: water-sports | size=4094, wc=328 |
| ✓ PASS | Size > wordcount: gardening.ht | size=4014, wc=321 |
| ✓ PASS | Size > wordcount: redirect-loo | size=1437, wc=76 |
| ✓ PASS | Size > wordcount: homemade-pas | size=3807, wc=294 |
| ✓ PASS | Size > wordcount: meta-desc-mi | size=1665, wc=120 |
| ✓ PASS | Size > wordcount: meta-desc-du | size=1823, wc=121 |
| ✓ PASS | Size > wordcount: painting.htm | size=3310, wc=231 |
| ✓ PASS | Size > wordcount: root | size=4554, wc=315 |
| ✓ PASS | max_depth >= 0 | max_depth=10 |
| ✓ PASS | Inlinks: some pages have inlinks > 0 | max=111 |
| ✓ PASS | Outlinks: some pages have outlinks > 0 | max=21 |
| ✓ PASS | Filter col 'img_count' in columns (Images) | exists=True |
| ✓ PASS | Shallow pages avg outlinks (21) | avg=21 |
| ✓ PASS | Deep pages avg outlinks (9) | avg=9 |
| ✓ PASS | Long2 wordcount: text-ratio | type=int |
| ✓ PASS | Long2 num_inlinks_uniq: text-ratio | type=int |
| ✓ PASS | Long2 num_outlinks_uni: text-ratio | type=int |
| ✓ PASS | Long2 wordcount: canonical- | type=int |
| ✓ PASS | Long2 num_inlinks_uniq: canonical- | type=int |
| ✓ PASS | Long2 num_outlinks_uni: canonical- | type=int |
| ✓ PASS | Float2 text_ratio_per: text-ratio | |
| ✓ PASS | Float2 text_ratio_per: canonical- | |
| ✓ PASS | Float2 text_ratio_per: water-spor | |
| ✓ PASS | Depth 1: avg outlinks 21 | count=1 |
| ✓ PASS | Depth 2: avg outlinks 9 | count=9 |
| ✓ PASS | Nofollow outlinks int: text-ratio-l | val=0 |
| ✓ PASS | Nofollow outlinks int: canonical-ot | val=0 |
| ✓ PASS | Nofollow outlinks int: water-sports | val=0 |
| ✓ PASS | SEO type 'wordcount': ok | total=190 |
| ✓ PASS | Has wordcount: text-ratio | |
| ✓ PASS | Has wordcount: canonical- | |
| ✓ PASS | Has wordcount: water-spor | |
| ✓ PASS | Has wordcount: gardening. | |
| ✓ PASS | Has wordcount: redirect-l | |
| ✓ PASS | Has wordcount: homemade-p | |
| ✓ PASS | Has wordcount: meta-desc- | |
| ✓ PASS | Has wordcount: meta-desc- | |
| ✓ PASS | Has wordcount: painting.h | |
| ✓ PASS | Has wordcount: root | |
| ✓ PASS | Has text_ratio: text-ratio | |
| ✓ PASS | Has text_ratio: canonical- | |
| ✓ PASS | Has text_ratio: water-spor | |
| ✓ PASS | Has text_ratio: gardening. | |
| ✓ PASS | Has text_ratio: redirect-l | |
| ✓ PASS | Has text_ratio: homemade-p | |
| ✓ PASS | Has text_ratio: meta-desc- | |
| ✓ PASS | Has text_ratio: meta-desc- | |
| ✓ PASS | Has text_ratio: painting.h | |
| ✓ PASS | Has text_ratio: root | |
| ✓ PASS | Core SEO wordcount: text-ratio | present=True |
| ✓ PASS | Core SEO text_ratio_p: text-ratio | present=True |
| ✓ PASS | Core SEO wordcount: canonical- | present=True |
| ✓ PASS | Core SEO text_ratio_p: canonical- | present=True |
| ✓ PASS | Core SEO wordcount: water-spor | present=True |
| ✓ PASS | Core SEO text_ratio_p: water-spor | present=True |
| ✓ PASS | Core SEO wordcount: gardening. | present=True |
| ✓ PASS | Core SEO text_ratio_p: gardening. | present=True |
| ✓ PASS | Core SEO wordcount: redirect-l | present=True |
| ✓ PASS | Core SEO text_ratio_p: redirect-l | present=True |
| ✓ PASS | Has inlinks: text-ratio | |
| ✓ PASS | Has outlinks: text-ratio | |
| ✓ PASS | Has inlinks: canonical- | |
| ✓ PASS | Has outlinks: canonical- | |
| ✓ PASS | Has inlinks: water-spor | |
| ✓ PASS | Has outlinks: water-spor | |
| ✓ PASS | Has inlinks: gardening. | |
| ✓ PASS | Has outlinks: gardening. | |
| ✓ PASS | Has inlinks: redirect-l | |
| ✓ PASS | Has outlinks: redirect-l | |
| ✓ PASS | Has inlinks: homemade-p | |
| ✓ PASS | Has outlinks: homemade-p | |
| ✓ PASS | Has inlinks: meta-desc- | |
| ✓ PASS | Has outlinks: meta-desc- | |
| ✓ PASS | Has inlinks: meta-desc- | |
| ✓ PASS | Has outlinks: meta-desc- | |
| ✓ PASS | Has inlinks: painting.h | |
| ✓ PASS | Has outlinks: painting.h | |
| ✓ PASS | Has inlinks: root | |
| ✓ PASS | Has outlinks: root | |
| ✓ PASS | Has nfo_outlinks: text-ratio | |
| ✓ PASS | Has nfo_outlinks: canonical- | |
| ✓ PASS | Has nfo_outlinks: water-spor | |
| ✓ PASS | Has nfo_outlinks: gardening. | |
| ✓ PASS | Has nfo_outlinks: redirect-l | |
| ✓ PASS | Export all_inlinks csv returns response | type=dict |
| ✓ PASS | Export all_inlinks csv has id or status | keys=['export_id', 'ok'] |
| ✓ PASS | Export all_inlinks xlsx returns response | type=dict |
| ✓ PASS | Export all_inlinks xlsx has id or status | keys=['export_id', 'ok'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Orderable columns: 71 | orderable=71, non=0 |
| ✓ PASS | 200 test sections milestone | Section 200 reached! |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status URL in project URLs | status_url=nginx-website-for-test.ttfb.ovh, proj_urls=1 |
| ✓ PASS | Status URL is valid domain | url=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Full crawl URL is valid | full=https://nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Status URL in project URLs | url=nginx-website-for-test.ttfb.ovh |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Wordcount: all >= 0 | |
| ✓ PASS | Wordcount: some > 50 (content pages) | max=328 |
| ✓ PASS | Wordcount: avg > 10 | avg=196 |
| ✓ PASS | Wordcount: max < 50000 | max=328 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Pagerank: all >= 0 | |
| ✓ PASS | Pagerank: max > 0 (some pages have rank) | max=50.7811 |
| ✓ PASS | Pagerank: all <= 1 (normalized) | max=50.7811 |
| ✓ PASS | Pagerank: variance exists | min=1.6468, max=50.7811 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Triple profile: email identical | |
| ✓ PASS | Triple profile: user_id identical | |
| ✓ PASS | Triple profile: quota_limit identical |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status notification is null or string | type=NoneType |
| ✓ PASS | Status host_entry is list | type=list, len=0 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Filter col 'accessed_at' in columns (All) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Security) | exists=True |
| ✓ PASS | Filter col 'url' in columns (Security) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Security) | exists=True |
| ✓ PASS | Filter col 'url' in columns (Security) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (URLs Containing) | exists=True |
| ✓ PASS | Filter col 'url' in columns (URLs Containing) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (URLs Containing) | exists=True |
| ✓ PASS | Filter col 'url' in columns (URLs Containing) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (URLs Containing) | exists=True |
| ✓ PASS | Filter col 'url' in columns (URLs Containing) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Status Codes) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Status Codes) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Status Codes) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Status Codes) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Status Codes) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Status Codes) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Status Codes) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Status Codes) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Status Codes) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Status Codes) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Status Codes) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Page Titles) | exists=True |
| ✓ PASS | Filter col 'title' in columns (Page Titles) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Page Titles) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Page Titles) | exists=True |
| ✓ PASS | Filter col 'duplicate.title' in columns (Page Titles) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Page Titles) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Page Titles) | exists=True |
| ✓ PASS | Filter col 'title_length' in columns (Page Titles) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Page Titles) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Page Titles) | exists=True |
| ✓ PASS | Filter col 'title_length' in columns (Page Titles) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Page Titles) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Status code by ) | exists=True |
| ✓ PASS | Filter col 'depth' in columns (Status code by ) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Status code by ) | exists=True |
| ✓ PASS | Filter col 'depth' in columns (Status code by ) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Status code by ) | exists=True |
| ✓ PASS | Filter col 'depth' in columns (Status code by ) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Status code by ) | exists=True |
| ✓ PASS | Filter col 'depth' in columns (Status code by ) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Status code by ) | exists=True |
| ✓ PASS | Filter col 'depth' in columns (Status code by ) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Meta Descriptio) | exists=True |
| ✓ PASS | Filter col 'meta_descriptio' in columns (Meta Descriptio) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Meta Descriptio) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Meta Descriptio) | exists=True |
| ✓ PASS | Filter col 'duplicate.meta_' in columns (Meta Descriptio) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Meta Descriptio) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Meta Descriptio) | exists=True |
| ✓ PASS | Filter col 'meta_descriptio' in columns (Meta Descriptio) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Meta Descriptio) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Meta Descriptio) | exists=True |
| ✓ PASS | Filter col 'meta_descriptio' in columns (Meta Descriptio) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Meta Descriptio) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (H1) | exists=True |
| ✓ PASS | Filter col 'h1_first' in columns (H1) | exists=True |
| ✓ PASS | Filter col 'status' in columns (H1) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (H1) | exists=True |
| ✓ PASS | Filter col 'duplicate.h1_fi' in columns (H1) | exists=True |
| ✓ PASS | Filter col 'status' in columns (H1) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (H1) | exists=True |
| ✓ PASS | Filter col 'h1_first_length' in columns (H1) | exists=True |
| ✓ PASS | Filter col 'status' in columns (H1) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (H1) | exists=True |
| ✓ PASS | Filter col 'title_same_as_h' in columns (H1) | exists=True |
| ✓ PASS | Filter col 'status' in columns (H1) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Canonical) | exists=True |
| ✓ PASS | Filter col 'canonical_link_' in columns (Canonical) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Canonical) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Canonical) | exists=True |
| ✓ PASS | Filter col 'canonical_not_m' in columns (Canonical) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Canonical) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Canonical) | exists=True |
| ✓ PASS | Filter col 'canonical_link_' in columns (Canonical) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Canonical) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Canonical) | exists=True |
| ✓ PASS | Filter col 'canonical_link_' in columns (Canonical) | exists=True |
| ✓ PASS | Filter col 'indexable_bool' in columns (Canonical) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Canonical) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Directives Robo) | exists=True |
| ✓ PASS | Filter col 'meta_robots' in columns (Directives Robo) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Directives Robo) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Directives Robo) | exists=True |
| ✓ PASS | Filter col 'meta_robots' in columns (Directives Robo) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Directives Robo) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Directives Robo) | exists=True |
| ✓ PASS | Filter col 'meta_robots' in columns (Directives Robo) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Directives Robo) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Directives Robo) | exists=True |
| ✓ PASS | Filter col 'meta_robots' in columns (Directives Robo) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Directives Robo) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Directives Robo) | exists=True |
| ✓ PASS | Filter col 'meta_robots' in columns (Directives Robo) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Directives Robo) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Hreflang) | exists=True |
| ✓ PASS | Filter col 'hreflang_count' in columns (Hreflang) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Hreflang) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Hreflang) | exists=True |
| ✓ PASS | Filter col 'hreflang_count' in columns (Hreflang) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Hreflang) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Pagination) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Pagination) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Images) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Images) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Images) | exists=True |
| ✓ PASS | Filter col 'imgs_without_al' in columns (Images) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Images) | exists=True |
| ✓ PASS | Filter col 'accessed_at' in columns (Performance) | exists=True |
| ✓ PASS | Filter col 'ttfb' in columns (Performance) | exists=True |
| ✓ PASS | Filter col 'status' in columns (Performance) | exists=True |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | No double slash: text-ratio-l | path=nginx-website-for-test.ttfb.ov |
| ✓ PASS | No query params: text-ratio-l | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | No fragments: text-ratio-l | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | No double slash: canonical-ot | path=nginx-website-for-test.ttfb.ov |
| ✓ PASS | No query params: canonical-ot | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | No fragments: canonical-ot | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | No double slash: water-sports | path=nginx-website-for-test.ttfb.ov |
| ✓ PASS | No query params: water-sports | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | No fragments: water-sports | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | No double slash: gardening.ht | path=nginx-website-for-test.ttfb.ov |
| ✓ PASS | No query params: gardening.ht | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | No fragments: gardening.ht | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | No double slash: redirect-loo | path=nginx-website-for-test.ttfb.ov |
| ✓ PASS | No query params: redirect-loo | url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | No fragments: redirect-loo | url=https://nginx-website-for-test.ttfb.ovh/ |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Projects API returns response | |
| ✓ PASS | Projects list is non-empty | count=538 |
| ✓ PASS | Projects list has ok field | ok=True |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Quota limit > 0 | limit=100 |
| ✓ PASS | Quota usage is int | usage=570 |
| ✓ PASS | Paid flag is boolean | paid=False |
| ✓ PASS | Quota usage and limit are numeric | usage=570, limit=100 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | found_in_crawl bool: text-ratio-l | type=bool |
| ✓ PASS | found_in_crawl bool: canonical-ot | type=bool |
| ✓ PASS | found_in_crawl bool: water-sports | type=bool |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Required event 'deployment' exists | found=True |
| ✓ PASS | Required event 'saving_results' exists | found=True |
| ✓ PASS | Required event 'crawl' exists | found=True |
| ✓ PASS | Required event 'read_crawl_data' exists | found=True |
| ✓ PASS | Total event types: 8 | types=['calculating_inlinks', 'calculating_pagerank', 'canonical_and_redirect_lists', 'crawl', 'deployment', 'find_duplicate', 'read_crawl_data', 'saving_results'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | dir1 is string: text-ratio-l | dir1=seo-tests |
| ✓ PASS | dir1 in URL path: text-ratio-l | dir1=seo-tests |
| ✓ PASS | dir1 is string: canonical-ot | dir1=seo-tests |
| ✓ PASS | dir1 in URL path: canonical-ot | dir1=seo-tests |
| ✓ PASS | dir1 is string: water-sports | dir1=sports |
| ✓ PASS | dir1 in URL path: water-sports | dir1=sports |
| ✓ PASS | dir1 is string: gardening.ht | dir1=nature |
| ✓ PASS | dir1 in URL path: gardening.ht | dir1=nature |
| ✓ PASS | dir1 is string: redirect-loo | dir1=seo-tests |
| ✓ PASS | dir1 in URL path: redirect-loo | dir1=seo-tests |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Iteration 111: 2287 tests, 210+ sections | count=2287 |
| ✓ PASS | 63+ endpoints, 42 CRUD ops maintained | verified |
| ✓ PASS | ITER 120: 63+ endpoints, 42 CRUD | maintained |
| ✓ PASS | ITER 130: 240+ sections, 63+ endpoints | sections=243, endpoints=63+ |
| ✓ PASS | ITER 140: 250+ sections, 63+ endpoints, 42 CRUD | all maintained |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | MIME format valid: text-ratio-l | ct=text/html |
| ✓ PASS | MIME format valid: canonical-ot | ct=text/html |
| ✓ PASS | MIME format valid: water-sports | ct=text/html |
| ✓ PASS | MIME format valid: gardening.ht | ct=text/html |
| ✓ PASS | MIME format valid: redirect-loo | ct=text/html |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status code distribution counted | dist={'2xx': 10} |
| ✓ PASS | Most records are 2xx | 2xx=10/10 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Indexable has no block: text-ratio-l | reason=None |
| ✓ PASS | Blocked reason present: canonical-ot | reason=Canonical |
| ✓ PASS | Indexable has no block: water-sports | reason=None |
| ✓ PASS | Indexable has no block: gardening.ht | reason=None |
| ✓ PASS | Indexable has no block: redirect-loo | reason=None |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Meta robots valid: text-ratio-low.html | meta_robots= |
| ✓ PASS | Meta robots valid: canonical-other.html | meta_robots= |
| ✓ PASS | Meta robots valid: water-sports.html | meta_robots= |
| ✓ PASS | Meta robots valid: gardening.html | meta_robots= |
| ✓ PASS | Meta robots valid: redirect-loop-target | meta_robots= |
| ✓ PASS | Meta robots valid: homemade-pastry.html | meta_robots= |
| ✓ PASS | Meta robots valid: meta-desc-missing.ht | meta_robots= |
| ✓ PASS | Meta robots valid: meta-desc-duplicate- | meta_robots= |
| ✓ PASS | Meta robots valid: painting.html | meta_robots= |
| ✓ PASS | Meta robots valid: root | meta_robots= |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Prefix filter /technology/: ok | filtered=190 |
| ✓ PASS | Prefix filter /travel/: ok | filtered=190 |
| ✓ PASS | Prefix filter /cooking/: ok | filtered=190 |
| ✓ PASS | Prefix filter /sports/: ok | filtered=190 |
| ✓ PASS | Prefix filter /health/: ok | filtered=190 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | URL len < 500: text-ratio-l | len=69 |
| ✓ PASS | URL len > 20: text-ratio-l | len=69 |
| ✓ PASS | Hostname in URL: text-ratio-l | hn=nginx-website-for-test.ttfb.ovh, url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | URL len < 500: canonical-ot | len=70 |
| ✓ PASS | URL len > 20: canonical-ot | len=70 |
| ✓ PASS | Hostname in URL: canonical-ot | hn=nginx-website-for-test.ttfb.ovh, url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | URL len < 500: water-sports | len=64 |
| ✓ PASS | URL len > 20: water-sports | len=64 |
| ✓ PASS | Hostname in URL: water-sports | hn=nginx-website-for-test.ttfb.ovh, url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | URL len < 500: gardening.ht | len=61 |
| ✓ PASS | URL len > 20: gardening.ht | len=61 |
| ✓ PASS | Hostname in URL: gardening.ht | hn=nginx-website-for-test.ttfb.ovh, url=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | URL len < 500: redirect-loo | len=75 |
| ✓ PASS | URL len > 20: redirect-loo | len=75 |
| ✓ PASS | Hostname in URL: redirect-loo | hn=nginx-website-for-test.ttfb.ovh, url=https://nginx-website-for-test.ttfb.ovh/ |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Title lengths: all >= 0 | |
| ✓ PASS | Title lengths: some > 20 (real titles) | max=55 |
| ✓ PASS | Title lengths: none > 200 | max=55 |
| ✓ PASS | Title lengths: avg between 10-80 | avg=40 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Total insight items: 42 | count=42 |
| ✓ PASS | No empty insight sections | empty=[] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Meta desc lengths: all >= 0 | |
| ✓ PASS | Meta desc lengths: max < 500 | max=105 |
| ✓ PASS | H1 lengths: all >= 0 | |
| ✓ PASS | H1 lengths: max < 200 | max=33 |
| ✓ PASS | H1 lengths: some > 3 (real headings) | max=33 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | ITER 120: 2343 tests (60% of 200 iterations) | +2195 from start |
| ✓ PASS | ITER 120: 100.0% pass rate | |
| ✓ PASS | ITER 120: 220+ sections | 225 sections |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Canonical starts https: text-ratio-l | canon=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | Canonical has domain: text-ratio-l | canon=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | Canonical starts https: canonical-ot | canon=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | Canonical has domain: canonical-ot | canon=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | Canonical starts https: redirect-loo | canon=https://nginx-website-for-test.ttfb.ovh/ |
| ✓ PASS | Canonical has domain: redirect-loo | canon=https://nginx-website-for-test.ttfb.ovh/ |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Schedule 'nginx-test-site' has valid frequency | freq=weekly |
| ✓ PASS | Schedule 'nginx-test-site' DOW valid (0-6) | dow=0 |
| ✓ PASS | Schedule 'nginx-test-site' has time | time=01:00 |
| ✓ PASS | Schedule 'nginx-test-site' has valid frequency | freq=weekly |
| ✓ PASS | Schedule 'nginx-test-site' DOW valid (0-6) | dow=0 |
| ✓ PASS | Schedule 'nginx-test-site' has time | time=01:00 |
| ✓ PASS | Schedule 'nginx-test-site' has valid frequency | freq=weekly |
| ✓ PASS | Schedule 'nginx-test-site' DOW valid (0-6) | dow=0 |
| ✓ PASS | Schedule 'nginx-test-site' has time | time=01:00 |
| ✓ PASS | Schedule 'nginx-test-s' next_run is datetime | next_run=2026-04-13T01:00:00+00:00 |
| ✓ PASS | Schedule 'nginx-test-s' last_run valid | last_run=None |
| ✓ PASS | Schedule 'nginx-test-s' next_run is datetime | next_run=2026-04-13T01:00:00+00:00 |
| ✓ PASS | Schedule 'nginx-test-s' last_run valid | last_run=None |
| ✓ PASS | Schedule 'nginx-test-s' next_run is datetime | next_run=2026-04-13T01:00:00+00:00 |
| ✓ PASS | Schedule 'nginx-test-s' last_run valid | last_run=None |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Insight section 'All' exists | found=True |
| ✓ PASS | Insight section 'Security' exists | found=True |
| ✓ PASS | Insight section 'Status Codes' exists | found=True |
| ✓ PASS | Insight section 'Page Titles' exists | found=True |
| ✓ PASS | Insight section 'Meta Description' exists | found=True |
| ✓ PASS | Insight section 'H1' exists | found=True |
| ✓ PASS | Insight section 'Canonical' exists | found=True |
| ✓ PASS | Insight section 'Directives Robots' exists | found=True |
| ✓ PASS | Insight section 'Hreflang' exists | found=True |
| ✓ PASS | Insight section 'Pagination' exists | found=True |
| ✓ PASS | Insight section 'Images' exists | found=True |
| ✓ PASS | Insight section 'Performance' exists | found=True |
| ✓ PASS | Insight 'All' has insights array | count=1 |
| ✓ PASS | Insight 'All' items have ident | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Insight 'Security' has insights array | count=2 |
| ✓ PASS | Insight 'Security' items have ident | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Insight 'URLs Containing Non-standard Characters' has insights array | count=3 |
| ✓ PASS | Insight 'URLs Containing Non-standard Characters' items have ident | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Insight 'Status Codes' has insights array | count=4 |
| ✓ PASS | Insight 'Status Codes' items have ident | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Insight 'Page Titles' has insights array | count=4 |
| ✓ PASS | Insight 'Page Titles' items have ident | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Insight 'All' has title+insights | title=True, insights=True |
| ✓ PASS | Insight 'Security' has title+insights | title=True, insights=True |
| ✓ PASS | Insight 'URLs Containing Non-standard Characters' has title+insights | title=True, insights=True |
| ✓ PASS | Insight 'Status Codes' has title+insights | title=True, insights=True |
| ✓ PASS | Insight 'Page Titles' has title+insights | title=True, insights=True |
| ✓ PASS | Insight sections count ~14 | count=14 |
| ✓ PASS | Insight sections list all names | count=14, names=['All', 'Security', 'URLs Containing Non-standard Characters', 'Status Codes', 'Page Titles'] |
| ✓ PASS | Insight section names unique and non-empty | unique=14/14 |
| ✓ PASS | Insight section sizes vary | min=1, max=5 |
| ✓ PASS | Insight sections have stable order | first='All', last='Performance' |
| ✓ PASS | Insight sections well-formed (title + insights array) | wellformed=14/14 |
| ✓ PASS | Insight sections in expected range (10-20) | count=14 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | All 13 endpoints respond (parallel) | got=13 |
| ✓ PASS | Parallel health < 5s | ms=345 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | 200 → typically indexable: text-ratio-l | status=200, indexable=True |
| ✓ PASS | 200 → typically indexable: canonical-ot | status=200, indexable=False |
| ✓ PASS | 200 → typically indexable: water-sports | status=200, indexable=True |
| ✓ PASS | 200 → typically indexable: gardening.ht | status=200, indexable=True |
| ✓ PASS | 200 → typically indexable: redirect-loo | status=200, indexable=True |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Column titles mostly unique (71/71) | unique=71 |
| ✓ PASS | No column title > 100 chars | long=0 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Size consistent crawl: <10% diff | s1=2170, s2=2170, diff=0.0% |
| ✓ PASS | Size consistent profile: <10% diff | s1=205, s2=205, diff=0.0% |
| ✓ PASS | Size consistent crawl: <10% diff | s1=6675, s2=6675, diff=0.0% |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Title+H1 both present: text-ratio-l | |
| ✓ PASS | Title+H1 both present: canonical-ot | |
| ✓ PASS | Title+H1 both present: water-sports | |
| ✓ PASS | Title+H1 both present: gardening.ht | |
| ✓ PASS | Title+H1 both present: redirect-loo |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Words/KB reasonable: text-ratio-l | wc=30, size=2544, w/kb=12.1 |
| ✓ PASS | Words/KB reasonable: canonical-ot | wc=125, size=1804, w/kb=71.0 |
| ✓ PASS | Words/KB reasonable: water-sports | wc=328, size=4094, w/kb=82.0 |
| ✓ PASS | Words/KB reasonable: gardening.ht | wc=321, size=4014, w/kb=81.9 |
| ✓ PASS | Words/KB reasonable: redirect-loo | wc=76, size=1437, w/kb=54.2 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Canon match flag: text-ratio-l | canon_matches=True, flag=False |
| ✓ PASS | Canon match flag: canonical-ot | canon_matches=False, flag=False |
| ✓ PASS | Canon match flag: redirect-loo | canon_matches=True, flag=False |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Mobile (375px): crawl page loads | text_len=1235 |
| ✓ PASS | Mobile (375px): project name visible | nginx not found |
| ✓ PASS | Mobile (375px): no horizontal overflow | page wider than viewport |
| ✓ PASS | Tablet (768px): crawl page loads | text_len=1235 |
| ✓ PASS | Tablet (768px): sidebar navigation visible | sidebar nav items not found |
| ✓ PASS | Tablet (768px): URL count visible |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Both bool: text-ratio-l | |
| ✓ PASS | Both bool: canonical-ot | |
| ✓ PASS | Both bool: water-sports | |
| ✓ PASS | Both bool: gardening.ht | |
| ✓ PASS | Both bool: redirect-loo |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Filters array: All/total_rows | type=list |
| ✓ PASS | Filter has column: All/total_rows | keys=['column', 'query', 'value'] |
| ✓ PASS | Filters array: Security/url_http | type=list |
| ✓ PASS | Filter has column: Security/url_http | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: Security/url_http | keys=['column', 'query', 'value'] |
| ✓ PASS | Filters array: Security/url_https | type=list |
| ✓ PASS | Filter has column: Security/url_https | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: Security/url_https | keys=['column', 'query', 'value'] |
| ✓ PASS | Filters array: URLs Contain/url_non_asci | type=list |
| ✓ PASS | Filter has column: URLs Contain/url_non_asci | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: URLs Contain/url_non_asci | keys=['column', 'query', 'value'] |
| ✓ PASS | Filters array: URLs Contain/url_undersco | type=list |
| ✓ PASS | Filter has column: URLs Contain/url_undersco | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: URLs Contain/url_undersco | keys=['column', 'query', 'value'] |
| ✓ PASS | Filters array: URLs Contain/url_uppercas | type=list |
| ✓ PASS | Filter has column: URLs Contain/url_uppercas | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: URLs Contain/url_uppercas | keys=['column', 'query', 'value'] |
| ✓ PASS | Filters array: Status Codes/url_success | type=list |
| ✓ PASS | Filter has column: Status Codes/url_success | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: Status Codes/url_success | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: Status Codes/url_success | keys=['column', 'query', 'value'] |
| ✓ PASS | Filters array: Status Codes/url_redirect | type=list |
| ✓ PASS | Filter has column: Status Codes/url_redirect | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: Status Codes/url_redirect | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: Status Codes/url_redirect | keys=['column', 'query', 'value'] |
| ✓ PASS | Filters array: Status Codes/url_client_e | type=list |
| ✓ PASS | Filter has column: Status Codes/url_client_e | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: Status Codes/url_client_e | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: Status Codes/url_client_e | keys=['column', 'query', 'value'] |
| ✓ PASS | Filters array: Page Titles/url_title_mi | type=list |
| ✓ PASS | Filter has column: Page Titles/url_title_mi | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: Page Titles/url_title_mi | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: Page Titles/url_title_mi | keys=['column', 'query', 'value'] |
| ✓ PASS | Filters array: Page Titles/url_title_du | type=list |
| ✓ PASS | Filter has column: Page Titles/url_title_du | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: Page Titles/url_title_du | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: Page Titles/url_title_du | keys=['column', 'query', 'value'] |
| ✓ PASS | Filters array: Page Titles/url_title_lo | type=list |
| ✓ PASS | Filter has column: Page Titles/url_title_lo | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: Page Titles/url_title_lo | keys=['column', 'query', 'value'] |
| ✓ PASS | Filter has column: Page Titles/url_title_lo | keys=['column', 'query', 'value'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | ITER 130: 2452 tests (65% of 200) | +2304 from start |
| ✓ PASS | ITER 130: pass rate 100.0% | |
| ✓ PASS | ITER 130: suite stable at ~230s | performance maintained |
| ✓ PASS | ITER 130: 130 git commits | continuous improvement |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Fields match columns: text-ratio-l | unknown=12: ['num_no_follow_outlinks', 'url_path', 'nofollow_links', 'crawl_start_date_year_month_day_hour', 'content_type'] |
| ✓ PASS | Fields match columns: canonical-ot | unknown=13: ['num_no_follow_outlinks', 'url_path', 'nofollow_links', 'crawl_start_date_year_month_day_hour', 'content_type'] |
| ✓ PASS | Columns present in data: 56/71 | pct=79% |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | URL count 100-300 (test site range) | url_count=190 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Title non-empty str: text-ratio-l | len=25 |
| ✓ PASS | H1 non-empty str: text-ratio-l | len=19 |
| ✓ PASS | Title non-empty str: canonical-ot | len=26 |
| ✓ PASS | H1 non-empty str: canonical-ot | len=33 |
| ✓ PASS | Title non-empty str: water-sports | len=46 |
| ✓ PASS | H1 non-empty str: water-sports | len=12 |
| ✓ PASS | Title non-empty str: gardening.ht | len=54 |
| ✓ PASS | H1 non-empty str: gardening.ht | len=20 |
| ✓ PASS | Title non-empty str: redirect-loo | len=31 |
| ✓ PASS | H1 non-empty str: redirect-loo | len=22 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Event 'crawl' has type/status/time | keys=['status', 'time', 'type'] |
| ✓ PASS | Event 'saving_results' has type/status/time | keys=['status', 'time', 'type'] |
| ✓ PASS | Event 'calculating_pagerank' has type/status/time | keys=['status', 'time', 'type'] |
| ✓ PASS | Event 'calculating_inlinks' has type/status/time | keys=['status', 'time', 'type'] |
| ✓ PASS | Event 'canonical_and_redirect_lists' has type/status/time | keys=['status', 'time', 'type'] |
| ✓ PASS | Event 'find_duplicate' has type/status/time | keys=['status', 'time', 'type'] |
| ✓ PASS | Event 'read_crawl_data' has type/status/time | keys=['status', 'time', 'type'] |
| ✓ PASS | Event 'deployment' has type/status/time | keys=['status', 'time', 'type'] |
| ✓ PASS | Event 'deployment' has status field | status=completed |
| ✓ PASS | Event 'deployment' has time field | time=Fri, 03 Apr 2026 13: |
| ✓ PASS | Event 'crawl' has status field | status=canceled |
| ✓ PASS | Event 'crawl' has time field | time=Mon, 06 Apr 2026 05: |
| ✓ PASS | Event 'read_crawl_data' has status field | status=completed |
| ✓ PASS | Event 'read_crawl_data' has time field | time=Fri, 03 Apr 2026 14: |
| ✓ PASS | Event 'calculating_inlinks' has status field | status=completed |
| ✓ PASS | Event 'calculating_inlinks' has time field | time=Fri, 03 Apr 2026 14: |
| ✓ PASS | Event 'calculating_pagerank' has status field | status=completed |
| ✓ PASS | Event 'calculating_pagerank' has time field | time=Fri, 03 Apr 2026 14: |
| ✓ PASS | Event 'find_duplicate' has status field | status=completed |
| ✓ PASS | Event 'find_duplicate' has time field | time=Fri, 03 Apr 2026 14: |
| ✓ PASS | Event 'canonical_and_redirect_lists' has status field | status=completed |
| ✓ PASS | Event 'canonical_and_redirect_lists' has time field | time=Fri, 03 Apr 2026 14: |
| ✓ PASS | Event 'saving_results' has status field | status=completed |
| ✓ PASS | Event 'saving_results' has time field | time=Fri, 03 Apr 2026 14: |
| ✓ PASS | Event 'crawl' has second precision | time=Mon, 06 Apr 2026 05:07:11 GMT |
| ✓ PASS | Event 'saving_resul' has second precision | time=Fri, 03 Apr 2026 14:01:13 GMT |
| ✓ PASS | Event 'calculating_' has second precision | time=Fri, 03 Apr 2026 14:01:12 GMT |
| ✓ PASS | Event 'calculating_' has second precision | time=Fri, 03 Apr 2026 14:01:12 GMT |
| ✓ PASS | Event 'canonical_an' has second precision | time=Fri, 03 Apr 2026 14:01:11 GMT |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Long2 size: text-ratio | type=int |
| ✓ PASS | Long2 num_redirects: text-ratio | type=int |
| ✓ PASS | Long2 hreflang_count: text-ratio | type=int |
| ✓ PASS | Long2 size: canonical- | type=int |
| ✓ PASS | Long2 num_redirects: canonical- | type=int |
| ✓ PASS | Long2 hreflang_count: canonical- | type=int |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | 🎉 Near 2500 tests: 2488 | count=2488 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Login page loads | url=https://crawler.kelo.gs/login |
| ✓ PASS | Login page has email input | |
| ✓ PASS | Login page has password input | |
| ✓ PASS | Login page has submit button | |
| ✓ PASS | Login page has signup link | |
| ✓ PASS | Login page sets cookies | count=0 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Sched 'nginx-test' created_at valid | |
| ✓ PASS | Sched 'nginx-test' created_at valid | |
| ✓ PASS | Sched 'nginx-test' created_at valid | |
| ✓ PASS | Float2 pagerank: text-ratio | |
| ✓ PASS | Float2 ttfb: text-ratio | |
| ✓ PASS | Float2 pagerank: canonical- | |
| ✓ PASS | Float2 ttfb: canonical- | |
| ✓ PASS | Float2 pagerank: water-spor | |
| ✓ PASS | Float2 ttfb: water-spor |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | URL valid ext: text-ratio-l | path=/text-ratio-low.html |
| ✓ PASS | URL valid ext: canonical-ot | path=canonical-other.html |
| ✓ PASS | URL valid ext: water-sports | path=ts/water-sports.html |
| ✓ PASS | URL valid ext: gardening.ht | path=ature/gardening.html |
| ✓ PASS | URL valid ext: redirect-loo | path=ect-loop-target.html |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | accessed_at has date: text-ratio-l | aa=2026-04-03T13:58:58 |
| ✓ PASS | accessed_at has date: canonical-ot | aa=2026-04-03T13:59:00 |
| ✓ PASS | accessed_at has date: water-sports | aa=2026-04-03T13:58:59 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | HTML page > 500B: text-ratio-l | size=2544, ct=text/html |
| ✓ PASS | HTML page > 500B: canonical-ot | size=1804, ct=text/html |
| ✓ PASS | HTML page > 500B: water-sports | size=4094, ct=text/html |
| ✓ PASS | HTML page > 500B: gardening.ht | size=4014, ct=text/html |
| ✓ PASS | HTML page > 500B: redirect-loo | size=1437, ct=text/html |
| ✓ PASS | Record has URL | url=https://nginx-website-for-test |
| ✓ PASS | Record has URL | url=https://nginx-website-for-test |
| ✓ PASS | Record has URL | url=https://nginx-website-for-test |
| ✓ PASS | Record has URL | url=https://nginx-website-for-test |
| ✓ PASS | Record has URL | url=https://nginx-website-for-test |
| ✓ PASS | Record has URL | url=https://nginx-website-for-test |
| ✓ PASS | Record has URL | url=https://nginx-website-for-test |
| ✓ PASS | Record has URL | url=https://nginx-website-for-test |
| ✓ PASS | Record has URL | url=https://nginx-website-for-test |
| ✓ PASS | Record has URL | url=https://nginx-website-for-test |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | ITER 140: 2524 tests (70% of 200) | +2376 from start |
| ✓ PASS | ITER 140: 100.0% pass rate | |
| ✓ PASS | ITER 140: runtime stable ~230s | optimized across 7 rounds |
| ✓ PASS | ITER 140: 140 git commits | continuous improvement since iteration 3 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | title_length == len(title): text-ratio-l | tl=25, actual=25 |
| ✓ PASS | h1_length == len(h1): text-ratio-l | hl=19, actual=19 |
| ✓ PASS | md_length == len(md): text-ratio-l | ml=40, actual=40 |
| ✓ PASS | title_length == len(title): canonical-ot | tl=26, actual=26 |
| ✓ PASS | h1_length == len(h1): canonical-ot | hl=33, actual=33 |
| ✓ PASS | md_length == len(md): canonical-ot | ml=54, actual=54 |
| ✓ PASS | title_length == len(title): water-sports | tl=46, actual=46 |
| ✓ PASS | h1_length == len(h1): water-sports | hl=12, actual=12 |
| ✓ PASS | title_length == len(title): gardening.ht | tl=54, actual=54 |
| ✓ PASS | h1_length == len(h1): gardening.ht | hl=20, actual=20 |
| ✓ PASS | title_length == len(title): redirect-loo | tl=31, actual=31 |
| ✓ PASS | h1_length == len(h1): redirect-loo | hl=22, actual=22 |
| ✓ PASS | md_length == len(md): redirect-loo | ml=30, actual=30 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | All records same crawl day | days={'2026-04-03'} |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Columns list has 71 entries | count=71 |
| ✓ PASS | All columns have 'data' field | 71/71 |
| ✓ PASS | All columns have 'title' field | 71/71 |
| ✓ PASS | Column data names are unique | unique=71, total=71 |
| ✓ PASS | All columns have consistent key structure | first_keys={'data', 'title', 'type', 'orderable'}, consistent=True |
| ✓ PASS | 'url' column has correct title | title=URL |
| ✓ PASS | 'status' column exists with title | title=Status |
| ✓ PASS | All columns have orderable field | missing=0 |
| ✓ PASS | All columns have non-empty titles | 71/71 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Depth 2 ~ 2 segments: text-ratio-l | depth=2, segments=2 |
| ✓ PASS | Depth 2 ~ 2 segments: canonical-ot | depth=2, segments=2 |
| ✓ PASS | Depth 2 ~ 2 segments: water-sports | depth=2, segments=2 |
| ✓ PASS | Depth 2 ~ 2 segments: gardening.ht | depth=2, segments=2 |
| ✓ PASS | Depth 2 ~ 2 segments: redirect-loo | depth=2, segments=2 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Iter 145: 2566 tests, 72.5% of 200 | |
| ✓ PASS | Iter 145: 260+ sections | 263 sections |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Canon self-ref check: text-ratio-l | self=True, cnm=False |
| ✓ PASS | Canon self-ref check: canonical-ot | self=False, cnm=False |
| ✓ PASS | Canon self-ref check: redirect-loo | self=True, cnm=False |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Project 'nginx-test-site' has 'id' | keys=['created_at', 'id', 'is_owner', 'last_crawl', 'name', 'next_crawl'] |
| ✓ PASS | Project 'nginx-test-site' has 'name' | keys=['created_at', 'id', 'is_owner', 'last_crawl', 'name', 'next_crawl'] |
| ✓ PASS | Project 'nginx-test-site' has 'is_owner' | keys=['created_at', 'id', 'is_owner', 'last_crawl', 'name', 'next_crawl'] |
| ✓ PASS | Project 'nginx-test-site' has 'num_of_crawls' | keys=['created_at', 'id', 'is_owner', 'last_crawl', 'name', 'next_crawl'] |
| ✓ PASS | Project 'qa-lifecycle-te' has 'id' | keys=['created_at', 'id', 'is_owner', 'last_crawl', 'name', 'next_crawl'] |
| ✓ PASS | Project 'qa-lifecycle-te' has 'name' | keys=['created_at', 'id', 'is_owner', 'last_crawl', 'name', 'next_crawl'] |
| ✓ PASS | Project 'qa-lifecycle-te' has 'is_owner' | keys=['created_at', 'id', 'is_owner', 'last_crawl', 'name', 'next_crawl'] |
| ✓ PASS | Project 'qa-lifecycle-te' has 'num_of_crawls' | keys=['created_at', 'id', 'is_owner', 'last_crawl', 'name', 'next_crawl'] |
| ✓ PASS | Project 'qa-lifecycle-te' has 'id' | keys=['created_at', 'id', 'is_owner', 'last_crawl', 'name', 'next_crawl'] |
| ✓ PASS | Project 'qa-lifecycle-te' has 'name' | keys=['created_at', 'id', 'is_owner', 'last_crawl', 'name', 'next_crawl'] |
| ✓ PASS | Project 'qa-lifecycle-te' has 'is_owner' | keys=['created_at', 'id', 'is_owner', 'last_crawl', 'name', 'next_crawl'] |
| ✓ PASS | Project 'qa-lifecycle-te' has 'num_of_crawls' | keys=['created_at', 'id', 'is_owner', 'last_crawl', 'name', 'next_crawl'] |
| ✓ PASS | Project 'alerts' is array | type=list, len=0 |
| ✓ PASS | Project 'comparisons' is array | type=list, len=0 |
| ✓ PASS | Project 'members' is array | type=list, len=0 |
| ✓ PASS | Project 'crawls' is array | type=list, len=8 |
| ✓ PASS | Project 'schedules' is array | type=list, len=5 |
| ✓ PASS | Project 'nginx-test-s' is_owner is bool | val=True |
| ✓ PASS | Project 'nginx-test-s' num_of_crawls is int | val=8 |
| ✓ PASS | Project 'qa-lifecycle' is_owner is bool | val=True |
| ✓ PASS | Project 'qa-lifecycle' num_of_crawls is int | val=0 |
| ✓ PASS | Project 'qa-lifecycle' is_owner is bool | val=True |
| ✓ PASS | Project 'qa-lifecycle' num_of_crawls is int | val=0 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | 200 has size: text-ratio-l | |
| ✓ PASS | 200 has size: canonical-ot | |
| ✓ PASS | 200 has size: water-sports | |
| ✓ PASS | 200 has size: gardening.ht | |
| ✓ PASS | 200 has size: redirect-loo |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Scraped field valid: text-ratio-l | type=dict |
| ✓ PASS | Nofollow links valid: text-ratio-l | type=list |
| ✓ PASS | Scraped field valid: canonical-ot | type=dict |
| ✓ PASS | H2 first is string: canonical-ot | type=str |
| ✓ PASS | Nofollow links valid: canonical-ot | type=list |
| ✓ PASS | Scraped field valid: water-sports | type=dict |
| ✓ PASS | H2 first is string: water-sports | type=str |
| ✓ PASS | Nofollow links valid: water-sports | type=list |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | RC 7780e6a5 has 'id' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC 7780e6a5 has 'status' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC 7780e6a5 has 'url' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC 7780e6a5 has 'project_name' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC 7780e6a5 has 'project_id' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC 7780e6a5 has 'created_at' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC d5af37bd has 'id' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC d5af37bd has 'status' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC d5af37bd has 'url' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC d5af37bd has 'project_name' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC d5af37bd has 'project_id' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC d5af37bd has 'created_at' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC a161d2da has 'id' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC a161d2da has 'status' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC a161d2da has 'url' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC a161d2da has 'project_name' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC a161d2da has 'project_id' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC a161d2da has 'created_at' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | RC 7780e6a5 URL is domain | url=nginx-website-for-test.ttfb.ov |
| ✓ PASS | RC d5af37bd URL is domain | url=nginx-website-for-test.ttfb.ov |
| ✓ PASS | RC a161d2da URL is domain | url=nginx-website-for-test.ttfb.ov |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Events are chronologically ordered | ascending=False, descending=True |
| ✓ PASS | Deployment before crawl event | deploy=2026-04-03 13:58:52+00:00, crawl=2026-04-06 05:07:11+00:00 |
| ✓ PASS | Saving results is latest processing event | save=2026-04-03 14:01:13+00:00, proc_max=2026-04-03 14:01:13+00:00 |
| ✓ PASS | Processing pipeline duration < 10 minutes | pipeline=141s |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | ITER 150: 2596 tests (75%) | |
| ✓ PASS | ITER 150: 270+ sections | 271 sections |
| ✓ PASS | ITER 150: 100% effective pass rate |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | SEO search 'meta-desc-missing': ok | total=190 |
| ✓ PASS | SEO search 'canonical-self': ok | total=190 |
| ✓ PASS | SEO search 'title-long': ok | total=190 |
| ✓ PASS | SEO search 'h1-missing': ok | total=190 |
| ✓ PASS | SEO search 'text-ratio-low': ok | total=190 |
| ✓ PASS | SEO search 'redirect-loop-targ': ok | total=190 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has status field: text-ratio-l | status=200 |
| ✓ PASS | Has status field: canonical-ot | status=200 |
| ✓ PASS | Has status field: water-sports | status=200 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Date year: text-ratio-low. | val=2026 |
| ✓ PASS | Date year: canonical-other | val=2026 |
| ✓ PASS | Date year: water-sports.ht | val=2026 |
| ✓ PASS | Date year: text-ratio | val=2026 |
| ✓ PASS | Date year: canonical- | val=2026 |
| ✓ PASS | Speed in valid set | speed=medium |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Extra bool data_in_both_crawl: text-ratio | |
| ✓ PASS | Extra bool data_in_both_crawl: text-ratio | |
| ✓ PASS | Extra bool data_in_both_crawl: canonical- | |
| ✓ PASS | Extra bool data_in_both_crawl: canonical- | |
| ✓ PASS | Owner ID is 36-char UUID | |
| ✓ PASS | Project ID is 36-char UUID |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | ITER 160: 2636 tests (80% complete) | |
| ✓ PASS | ITER 160: 278+ sections | 279 sections |
| ✓ PASS | ITER 160: 100% effective | |
| ✓ PASS | ITER 160: 160 commits | continuous |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Aggregate: 10 records sampled | |
| ✓ PASS | Aggregate: 10 have status | |
| ✓ PASS | Aggregate: avg TTFB 0.026s | |
| ✓ PASS | Aggregate: total size 28KB |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | data_only googl: text-ratio | |
| ✓ PASS | data_only googl: text-ratio | |
| ✓ PASS | data_only searc: text-ratio | |
| ✓ PASS | data_only searc: text-ratio | |
| ✓ PASS | data_only googl: canonical- | |
| ✓ PASS | data_only googl: canonical- | |
| ✓ PASS | data_only searc: canonical- | |
| ✓ PASS | data_only searc: canonical- |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status 'custom_robots_txt' null or string | type=NoneType |
| ✓ PASS | Status 'preprocess_rules' null or string | type=NoneType |
| ✓ PASS | Status 'scrape_rules' null or string | type=NoneType |
| ✓ PASS | Status 'speed_custom' null or string | type=NoneType |
| ✓ PASS | Status 'request_headers' null or string | type=NoneType |
| ✓ PASS | Status 'sitemap_urls' null or string | type=NoneType |
| ✓ PASS | Status 'notification' null or string | type=NoneType |
| ✓ PASS | Status 'inspection_site_ur' null or string | type=NoneType |
| ✓ PASS | Suite is 15173 lines | lines=15173 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | 200 page url: text-ratio | val=https://nginx-websit |
| ✓ PASS | 200 page hostname: text-ratio | val=nginx-website-for-te |
| ✓ PASS | 200 page title: text-ratio | val=Low Text Ratio - SEO |
| ✓ PASS | 200 page url: canonical- | val=https://nginx-websit |
| ✓ PASS | 200 page hostname: canonical- | val=nginx-website-for-te |
| ✓ PASS | 200 page title: canonical- | val=Canonical Other - SE |
| ✓ PASS | 200 page url: water-spor | val=https://nginx-websit |
| ✓ PASS | 200 page hostname: water-spor | val=nginx-website-for-te |
| ✓ PASS | 200 page title: water-spor | val=Water Sports - nginx |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Query 'missing' valid (All) | query=missing |
| ✓ PASS | Query 'missing' valid (Security) | query=missing |
| ✓ PASS | Query 'prefix' valid (Security) | query=prefix |
| ✓ PASS | Query 'missing' valid (Security) | query=missing |
| ✓ PASS | Query 'prefix' valid (Security) | query=prefix |
| ✓ PASS | Query 'missing' valid (URLs Contain) | query=missing |
| ✓ PASS | Query 'regex' valid (URLs Contain) | query=regex |
| ✓ PASS | Query 'missing' valid (URLs Contain) | query=missing |
| ✓ PASS | Query 'contains' valid (URLs Contain) | query=contains |
| ✓ PASS | Query 'missing' valid (URLs Contain) | query=missing |
| ✓ PASS | Query 'regex' valid (URLs Contain) | query=regex |
| ✓ PASS | Query 'missing' valid (Status Codes) | query=missing |
| ✓ PASS | Query 'greater_equal' valid (Status Codes) | query=greater_equal |
| ✓ PASS | Query 'less' valid (Status Codes) | query=less |
| ✓ PASS | Query 'missing' valid (Status Codes) | query=missing |
| ✓ PASS | Query 'greater_equal' valid (Status Codes) | query=greater_equal |
| ✓ PASS | Query 'less' valid (Status Codes) | query=less |
| ✓ PASS | Query 'missing' valid (Status Codes) | query=missing |
| ✓ PASS | Query 'greater_equal' valid (Status Codes) | query=greater_equal |
| ✓ PASS | Query 'less' valid (Status Codes) | query=less |
| ✓ PASS | Query 'missing' valid (Page Titles) | query=missing |
| ✓ PASS | Query 'missing' valid (Page Titles) | query=missing |
| ✓ PASS | Query 'exact_match' valid (Page Titles) | query=exact_match |
| ✓ PASS | Query 'missing' valid (Page Titles) | query=missing |
| ✓ PASS | Query 'exact_match' valid (Page Titles) | query=exact_match |
| ✓ PASS | Query 'exact_match' valid (Page Titles) | query=exact_match |
| ✓ PASS | Query 'missing' valid (Page Titles) | query=missing |
| ✓ PASS | Query 'greater' valid (Page Titles) | query=greater |
| ✓ PASS | Query 'exact_match' valid (Page Titles) | query=exact_match |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status buckets total: 10 | |
| ✓ PASS | 2xx is majority | dist={'1xx': 0, '2xx': 10, '3xx': 0, '4xx': 0, '5xx': 0} |
| ✓ PASS | Final: owner_id == user_id |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Sample has 10 URLs |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Record text-ratio has 40+ fields | keys=73 |
| ✓ PASS | Record canonical- has 40+ fields | keys=74 |
| ✓ PASS | Record water-spor has 40+ fields | keys=74 |
| ✓ PASS | Record has hostname | hn=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Record has hostname | hn=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Record has hostname | hn=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Record has hostname | hn=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Record has hostname | hn=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Record has hostname | hn=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Record has hostname | hn=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Record has hostname | hn=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Record has hostname | hn=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Record has hostname | hn=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Record has depth | depth=2 |
| ✓ PASS | Record has depth | depth=2 |
| ✓ PASS | Record has depth | depth=2 |
| ✓ PASS | Record has depth | depth=2 |
| ✓ PASS | Record has depth | depth=2 |
| ✓ PASS | Record has depth | depth=2 |
| ✓ PASS | Record has depth | depth=2 |
| ✓ PASS | Record has depth | depth=2 |
| ✓ PASS | Record has depth | depth=2 |
| ✓ PASS | Record has depth | depth=1 |
| ✓ PASS | Record 'text-ratio' has non-null fields | non_null=66/73 |
| ✓ PASS | Record 'canonical-' has non-null fields | non_null=68/74 |
| ✓ PASS | Record 'water-spor' has non-null fields | non_null=65/74 |
| ✓ PASS | Record 'gardening.' has non-null fields | non_null=65/74 |
| ✓ PASS | Record 'redirect-l' has non-null fields | non_null=66/73 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | SEO type 'hreflang': ok | total=190 |
| ✓ PASS | SEO type 'pagination': ok | total=190 |
| ✓ PASS | SEO type 'redirect': ok | total=190 |
| ✓ PASS | SEO type 'noindex': ok | total=190 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | ITER 170: 2715 tests (85% of 200) | |
| ✓ PASS | ITER 170: 288+ sections | 289 sections |
| ✓ PASS | ITER 170: 100% effective | |
| ✓ PASS | ITER 170: 30 iterations remaining | 170/200 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Project id matches PROJECT_ID | got=a74b27ce-68b3-44a7-9db9-18bc6f75b419 |
| ✓ PASS | Latest crawl has 'id' field | |
| ✓ PASS | Latest crawl has 'status' field | status=quota limit reached |
| ✓ PASS | Latest crawl has 'url' field | url=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Latest crawl has 'created_at' timestamp | created_at=Mon, 06 Apr 2026 01:00:05 GMT |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | All records same crawl_id (final) | ids={'f86b84ae-fa69-424e-8a04-ec7515125950'} |
| ✓ PASS | Status has mix of true/false booleans | |
| ✓ PASS | Status has multiple pipeline events | count=8 |
| ✓ PASS | Status has core required fields | present=['url', 'status', 'is_complete', 'created_at'], missing=['id', 'urls_crawled'] |
| ✓ PASS | Status has crawl config fields | found=['max_depth', 'user_agent'], count=2 |
| ✓ PASS | Status has core settings fields | found=['url', 'status', 'created_at', 'completed_at', 'is_complete', 'project_id'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Events total duration: 141s |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Column types: 5 distinct | types={'float', 'text', 'long', 'boolean', 'date'} |
| ✓ PASS | Date columns: 1 | cols=['accessed_at'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | URL protocol https: text-ratio | url=https://nginx-website-for |
| ✓ PASS | URL protocol https: canonical- | url=https://nginx-website-for |
| ✓ PASS | URL protocol https: water-spor | url=https://nginx-website-for |
| ✓ PASS | URL protocol https: gardening. | url=https://nginx-website-for |
| ✓ PASS | URL protocol https: redirect-l | url=https://nginx-website-for |
| ✓ PASS | Final: 2741 tests at iter 174 | |
| ✓ PASS | Final: 3800+ tests achieved | total=3812 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | ITER 175: 2752 tests (87.5%) | |
| ✓ PASS | ITER 175: 25 iterations remaining | 175/200 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has status: text-ratio | |
| ✓ PASS | Has TTFB: text-ratio | |
| ✓ PASS | Has status: canonical- | |
| ✓ PASS | Has TTFB: canonical- | |
| ✓ PASS | Has status: water-spor | |
| ✓ PASS | Has TTFB: water-spor | |
| ✓ PASS | Has status: gardening. | |
| ✓ PASS | Has TTFB: gardening. | |
| ✓ PASS | Has status: redirect-l | |
| ✓ PASS | Has TTFB: redirect-l | |
| ✓ PASS | Has status: homemade-p | |
| ✓ PASS | Has TTFB: homemade-p | |
| ✓ PASS | Has status: meta-desc- | |
| ✓ PASS | Has TTFB: meta-desc- | |
| ✓ PASS | Has status: meta-desc- | |
| ✓ PASS | Has TTFB: meta-desc- | |
| ✓ PASS | Has status: painting.h | |
| ✓ PASS | Has TTFB: painting.h | |
| ✓ PASS | Has status: root | |
| ✓ PASS | Has TTFB: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has size: text-ratio | |
| ✓ PASS | Has follow: text-ratio | |
| ✓ PASS | Has size: canonical- | |
| ✓ PASS | Has follow: canonical- | |
| ✓ PASS | Has size: water-spor | |
| ✓ PASS | Has follow: water-spor | |
| ✓ PASS | Has size: gardening. | |
| ✓ PASS | Has follow: gardening. | |
| ✓ PASS | Has size: redirect-l | |
| ✓ PASS | Has follow: redirect-l | |
| ✓ PASS | Has size: homemade-p | |
| ✓ PASS | Has follow: homemade-p | |
| ✓ PASS | Has size: meta-desc- | |
| ✓ PASS | Has follow: meta-desc- | |
| ✓ PASS | Has size: meta-desc- | |
| ✓ PASS | Has follow: meta-desc- | |
| ✓ PASS | Has size: painting.h | |
| ✓ PASS | Has follow: painting.h | |
| ✓ PASS | Has size: root | |
| ✓ PASS | Has follow: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has indexable: text-ratio | |
| ✓ PASS | Has pagerank: text-ratio | |
| ✓ PASS | Has indexable: canonical- | |
| ✓ PASS | Has pagerank: canonical- | |
| ✓ PASS | Has indexable: water-spor | |
| ✓ PASS | Has pagerank: water-spor | |
| ✓ PASS | Has indexable: gardening. | |
| ✓ PASS | Has pagerank: gardening. | |
| ✓ PASS | Has indexable: redirect-l | |
| ✓ PASS | Has pagerank: redirect-l | |
| ✓ PASS | Has indexable: homemade-p | |
| ✓ PASS | Has pagerank: homemade-p | |
| ✓ PASS | Has indexable: meta-desc- | |
| ✓ PASS | Has pagerank: meta-desc- | |
| ✓ PASS | Has indexable: meta-desc- | |
| ✓ PASS | Has pagerank: meta-desc- | |
| ✓ PASS | Has indexable: painting.h | |
| ✓ PASS | Has pagerank: painting.h | |
| ✓ PASS | Has indexable: root | |
| ✓ PASS | Has pagerank: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has found_in_sitemaps: text-ratio | |
| ✓ PASS | Has found_in_crawl: text-ratio | |
| ✓ PASS | Has found_in_sitemaps: canonical- | |
| ✓ PASS | Has found_in_crawl: canonical- | |
| ✓ PASS | Has found_in_sitemaps: water-spor | |
| ✓ PASS | Has found_in_crawl: water-spor | |
| ✓ PASS | Has found_in_sitemaps: gardening. | |
| ✓ PASS | Has found_in_crawl: gardening. | |
| ✓ PASS | Has found_in_sitemaps: redirect-l | |
| ✓ PASS | Has found_in_crawl: redirect-l | |
| ✓ PASS | Has found_in_sitemaps: homemade-p | |
| ✓ PASS | Has found_in_crawl: homemade-p | |
| ✓ PASS | Has found_in_sitemaps: meta-desc- | |
| ✓ PASS | Has found_in_crawl: meta-desc- | |
| ✓ PASS | Has found_in_sitemaps: meta-desc- | |
| ✓ PASS | Has found_in_crawl: meta-desc- | |
| ✓ PASS | Has found_in_sitemaps: painting.h | |
| ✓ PASS | Has found_in_crawl: painting.h | |
| ✓ PASS | Has found_in_sitemaps: root | |
| ✓ PASS | Has found_in_crawl: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Protocol = https | protocol=https |
| ✓ PASS | URL = nginx-website-for-test.ttfb.ovh | url=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | crawl_sitemaps enabled | |
| ✓ PASS | calculate_pagerank enabled | |
| ✓ PASS | crawl_alternate (hreflang) enabled | |
| ✓ PASS | crawl_redirect enabled | |
| ✓ PASS | JS rendering disabled | |
| ✓ PASS | Cookies disabled | |
| ✓ PASS | User agent is desktop | ua=desktop |
| ✓ PASS | Status field 'calculate_pagerank' is boolean | type=bool, val=True |
| ✓ PASS | Status field 'crawl_sitemaps' is boolean | type=bool, val=True |
| ✓ PASS | Status field 'crawl_alternate' is boolean | type=bool, val=True |
| ✓ PASS | Status field 'crawl_redirect' is boolean | type=bool, val=True |
| ✓ PASS | Protocol is HTTPS | |
| ✓ PASS | Consistency: status.crawl_sitemaps == dates.crawl_sitemaps | status=True, dates=True |
| ✓ PASS | Schedule has crawl_sitemaps config | val=True |
| ✓ PASS | Crawl 7780e6a5 has 'crawl_sitemaps' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl d5af37bd has 'crawl_sitemaps' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Crawl a161d2da has 'crawl_sitemaps' | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Dates crawl_sitemaps == Status | |
| ✓ PASS | Schedule config 'crawl_sitemaps' | val=True |
| ✓ PASS | Schedule config 'calculate_pagerank' | val=True |
| ✓ PASS | Schedule config 'crawl_alternate' | val=True |
| ✓ PASS | Schedule config 'crawl_redirect' | val=True |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Export item has id (id or export_id) | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type'] |
| ✓ PASS | Export item has status (status or completed) | completed=True |
| ✓ PASS | Export item has 'created_at' | |
| ✓ PASS | Export item has type/format | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type', 'project_id', 'project_name'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | ITER 180: 2855 tests (90%) | |
| ✓ PASS | ITER 180: 300 test sections | |
| ✓ PASS | ITER 180: 20 iterations remaining | final stretch! |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has title: text-ratio | |
| ✓ PASS | Has title_length: text-ratio | |
| ✓ PASS | Has md_length: text-ratio | |
| ✓ PASS | Has title: canonical- | |
| ✓ PASS | Has title_length: canonical- | |
| ✓ PASS | Has md_length: canonical- | |
| ✓ PASS | Has title: water-spor | |
| ✓ PASS | Has title_length: water-spor | |
| ✓ PASS | Has title: gardening. | |
| ✓ PASS | Has title_length: gardening. | |
| ✓ PASS | Has title: redirect-l | |
| ✓ PASS | Has title_length: redirect-l | |
| ✓ PASS | Has md_length: redirect-l | |
| ✓ PASS | Has title: homemade-p | |
| ✓ PASS | Has title_length: homemade-p | |
| ✓ PASS | Has title: meta-desc- | |
| ✓ PASS | Has title_length: meta-desc- | |
| ✓ PASS | Has title: meta-desc- | |
| ✓ PASS | Has title_length: meta-desc- | |
| ✓ PASS | Has md_length: meta-desc- | |
| ✓ PASS | Has title: painting.h | |
| ✓ PASS | Has title_length: painting.h | |
| ✓ PASS | Has title: root | |
| ✓ PASS | Has title_length: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has h1: text-ratio | |
| ✓ PASS | Has h1_length: text-ratio | |
| ✓ PASS | Has canonical field: text-ratio | |
| ✓ PASS | Has h1: canonical- | |
| ✓ PASS | Has h1_length: canonical- | |
| ✓ PASS | Has canonical field: canonical- | |
| ✓ PASS | Has h1: water-spor | |
| ✓ PASS | Has h1_length: water-spor | |
| ✓ PASS | Has canonical field: water-spor | |
| ✓ PASS | Has h1: gardening. | |
| ✓ PASS | Has h1_length: gardening. | |
| ✓ PASS | Has canonical field: gardening. | |
| ✓ PASS | Has h1: redirect-l | |
| ✓ PASS | Has h1_length: redirect-l | |
| ✓ PASS | Has canonical field: redirect-l | |
| ✓ PASS | Has h1: homemade-p | |
| ✓ PASS | Has h1_length: homemade-p | |
| ✓ PASS | Has canonical field: homemade-p | |
| ✓ PASS | Has h1: meta-desc- | |
| ✓ PASS | Has h1_length: meta-desc- | |
| ✓ PASS | Has canonical field: meta-desc- | |
| ✓ PASS | Has h1: meta-desc- | |
| ✓ PASS | Has h1_length: meta-desc- | |
| ✓ PASS | Has canonical field: meta-desc- | |
| ✓ PASS | Has h1: painting.h | |
| ✓ PASS | Has h1_length: painting.h | |
| ✓ PASS | Has canonical field: painting.h | |
| ✓ PASS | Has h1: root | |
| ✓ PASS | Has h1_length: root | |
| ✓ PASS | Has canonical field: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has content_type: text-ratio | |
| ✓ PASS | Has depth int: text-ratio | |
| ✓ PASS | Has accessed_at: text-ratio | |
| ✓ PASS | Has content_type: canonical- | |
| ✓ PASS | Has depth int: canonical- | |
| ✓ PASS | Has accessed_at: canonical- | |
| ✓ PASS | Has content_type: water-spor | |
| ✓ PASS | Has depth int: water-spor | |
| ✓ PASS | Has accessed_at: water-spor | |
| ✓ PASS | Has content_type: gardening. | |
| ✓ PASS | Has depth int: gardening. | |
| ✓ PASS | Has accessed_at: gardening. | |
| ✓ PASS | Has content_type: redirect-l | |
| ✓ PASS | Has depth int: redirect-l | |
| ✓ PASS | Has accessed_at: redirect-l | |
| ✓ PASS | Has content_type: homemade-p | |
| ✓ PASS | Has depth int: homemade-p | |
| ✓ PASS | Has accessed_at: homemade-p | |
| ✓ PASS | Has content_type: meta-desc- | |
| ✓ PASS | Has depth int: meta-desc- | |
| ✓ PASS | Has accessed_at: meta-desc- | |
| ✓ PASS | Has content_type: meta-desc- | |
| ✓ PASS | Has depth int: meta-desc- | |
| ✓ PASS | Has accessed_at: meta-desc- | |
| ✓ PASS | Has content_type: painting.h | |
| ✓ PASS | Has depth int: painting.h | |
| ✓ PASS | Has accessed_at: painting.h | |
| ✓ PASS | Has content_type: root | |
| ✓ PASS | Has depth int: root | |
| ✓ PASS | Has accessed_at: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Core SEO url: text-ratio | present=True |
| ✓ PASS | Core SEO status: text-ratio | present=True |
| ✓ PASS | Core SEO depth: text-ratio | present=True |
| ✓ PASS | Core SEO title: text-ratio | present=True |
| ✓ PASS | Core SEO h1_first: text-ratio | present=True |
| ✓ PASS | Core SEO indexable_bo: text-ratio | present=True |
| ✓ PASS | Core SEO follow: text-ratio | present=True |
| ✓ PASS | Core SEO ttfb: text-ratio | present=True |
| ✓ PASS | Core SEO size: text-ratio | present=True |
| ✓ PASS | Core SEO pagerank: text-ratio | present=True |
| ✓ PASS | Core SEO found_in_sit: text-ratio | present=True |
| ✓ PASS | Core SEO hostname: text-ratio | present=True |
| ✓ PASS | Core SEO url: canonical- | present=True |
| ✓ PASS | Core SEO status: canonical- | present=True |
| ✓ PASS | Core SEO depth: canonical- | present=True |
| ✓ PASS | Core SEO title: canonical- | present=True |
| ✓ PASS | Core SEO h1_first: canonical- | present=True |
| ✓ PASS | Core SEO indexable_bo: canonical- | present=True |
| ✓ PASS | Core SEO follow: canonical- | present=True |
| ✓ PASS | Core SEO ttfb: canonical- | present=True |
| ✓ PASS | Core SEO size: canonical- | present=True |
| ✓ PASS | Core SEO pagerank: canonical- | present=True |
| ✓ PASS | Core SEO found_in_sit: canonical- | present=True |
| ✓ PASS | Core SEO hostname: canonical- | present=True |
| ✓ PASS | Core SEO url: water-spor | present=True |
| ✓ PASS | Core SEO status: water-spor | present=True |
| ✓ PASS | Core SEO depth: water-spor | present=True |
| ✓ PASS | Core SEO title: water-spor | present=True |
| ✓ PASS | Core SEO h1_first: water-spor | present=True |
| ✓ PASS | Core SEO indexable_bo: water-spor | present=True |
| ✓ PASS | Core SEO follow: water-spor | present=True |
| ✓ PASS | Core SEO ttfb: water-spor | present=True |
| ✓ PASS | Core SEO size: water-spor | present=True |
| ✓ PASS | Core SEO pagerank: water-spor | present=True |
| ✓ PASS | Core SEO found_in_sit: water-spor | present=True |
| ✓ PASS | Core SEO hostname: water-spor | present=True |
| ✓ PASS | Core SEO url: gardening. | present=True |
| ✓ PASS | Core SEO status: gardening. | present=True |
| ✓ PASS | Core SEO depth: gardening. | present=True |
| ✓ PASS | Core SEO title: gardening. | present=True |
| ✓ PASS | Core SEO h1_first: gardening. | present=True |
| ✓ PASS | Core SEO indexable_bo: gardening. | present=True |
| ✓ PASS | Core SEO follow: gardening. | present=True |
| ✓ PASS | Core SEO ttfb: gardening. | present=True |
| ✓ PASS | Core SEO size: gardening. | present=True |
| ✓ PASS | Core SEO pagerank: gardening. | present=True |
| ✓ PASS | Core SEO found_in_sit: gardening. | present=True |
| ✓ PASS | Core SEO hostname: gardening. | present=True |
| ✓ PASS | Core SEO url: redirect-l | present=True |
| ✓ PASS | Core SEO status: redirect-l | present=True |
| ✓ PASS | Core SEO depth: redirect-l | present=True |
| ✓ PASS | Core SEO title: redirect-l | present=True |
| ✓ PASS | Core SEO h1_first: redirect-l | present=True |
| ✓ PASS | Core SEO indexable_bo: redirect-l | present=True |
| ✓ PASS | Core SEO follow: redirect-l | present=True |
| ✓ PASS | Core SEO ttfb: redirect-l | present=True |
| ✓ PASS | Core SEO size: redirect-l | present=True |
| ✓ PASS | Core SEO pagerank: redirect-l | present=True |
| ✓ PASS | Core SEO found_in_sit: redirect-l | present=True |
| ✓ PASS | Core SEO hostname: redirect-l | present=True |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | 🎉 3000 tests target: 3022 | count=3022 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has dup.level: text-ratio | |
| ✓ PASS | Has dir1: text-ratio | dir1=seo-tests |
| ✓ PASS | Has dup.level: canonical- | |
| ✓ PASS | Has dir1: canonical- | dir1=seo-tests |
| ✓ PASS | Has dup.level: water-spor | |
| ✓ PASS | Has dir1: water-spor | dir1=sports |
| ✓ PASS | Has dup.level: gardening. | |
| ✓ PASS | Has dir1: gardening. | dir1=nature |
| ✓ PASS | Has dup.level: redirect-l | |
| ✓ PASS | Has dir1: redirect-l | dir1=seo-tests |
| ✓ PASS | Has dup.level: homemade-p | |
| ✓ PASS | Has dir1: homemade-p | dir1=cooking |
| ✓ PASS | Has dup.level: meta-desc- | |
| ✓ PASS | Has dir1: meta-desc- | dir1=seo-tests |
| ✓ PASS | Has dup.level: meta-desc- | |
| ✓ PASS | Has dir1: meta-desc- | dir1=seo-tests |
| ✓ PASS | Has dup.level: painting.h | |
| ✓ PASS | Has dir1: painting.h | dir1=art |
| ✓ PASS | Has dup.level: root | |
| ✓ PASS | Has dir1: root | dir1=art |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has denied_robots: text-ratio | |
| ✓ PASS | Has denied_robots: canonical- | |
| ✓ PASS | Has denied_robots: water-spor | |
| ✓ PASS | Has denied_robots: gardening. | |
| ✓ PASS | Has denied_robots: redirect-l | |
| ✓ PASS | Has denied_robots: homemade-p | |
| ✓ PASS | Has denied_robots: meta-desc- | |
| ✓ PASS | Has denied_robots: meta-desc- | |
| ✓ PASS | Has denied_robots: painting.h | |
| ✓ PASS | Has denied_robots: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has discovery_src: text-ratio | |
| ✓ PASS | Has amp: text-ratio | |
| ✓ PASS | Has discovery_src: canonical- | |
| ✓ PASS | Has amp: canonical- | |
| ✓ PASS | Has discovery_src: water-spor | |
| ✓ PASS | Has amp: water-spor | |
| ✓ PASS | Has discovery_src: gardening. | |
| ✓ PASS | Has amp: gardening. | |
| ✓ PASS | Has discovery_src: redirect-l | |
| ✓ PASS | Has amp: redirect-l | |
| ✓ PASS | Has discovery_src: homemade-p | |
| ✓ PASS | Has amp: homemade-p | |
| ✓ PASS | Has discovery_src: meta-desc- | |
| ✓ PASS | Has amp: meta-desc- | |
| ✓ PASS | Has discovery_src: meta-desc- | |
| ✓ PASS | Has amp: meta-desc- | |
| ✓ PASS | Has discovery_src: painting.h | |
| ✓ PASS | Has amp: painting.h | |
| ✓ PASS | Has discovery_src: root | |
| ✓ PASS | Has amp: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has sitemap_only: text-ratio | |
| ✓ PASS | Has hreflang_cnt: text-ratio | |
| ✓ PASS | Has sitemap_only: canonical- | |
| ✓ PASS | Has hreflang_cnt: canonical- | |
| ✓ PASS | Has sitemap_only: water-spor | |
| ✓ PASS | Has hreflang_cnt: water-spor | |
| ✓ PASS | Has sitemap_only: gardening. | |
| ✓ PASS | Has hreflang_cnt: gardening. | |
| ✓ PASS | Has sitemap_only: redirect-l | |
| ✓ PASS | Has hreflang_cnt: redirect-l | |
| ✓ PASS | Has sitemap_only: homemade-p | |
| ✓ PASS | Has hreflang_cnt: homemade-p | |
| ✓ PASS | Has sitemap_only: meta-desc- | |
| ✓ PASS | Has hreflang_cnt: meta-desc- | |
| ✓ PASS | Has sitemap_only: meta-desc- | |
| ✓ PASS | Has hreflang_cnt: meta-desc- | |
| ✓ PASS | Has sitemap_only: painting.h | |
| ✓ PASS | Has hreflang_cnt: painting.h | |
| ✓ PASS | Has sitemap_only: root | |
| ✓ PASS | Has hreflang_cnt: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has title=h1: text-ratio | |
| ✓ PASS | Has canon_match: text-ratio | |
| ✓ PASS | Has num_redir: text-ratio | |
| ✓ PASS | Has title=h1: canonical- | |
| ✓ PASS | Has canon_match: canonical- | |
| ✓ PASS | Has num_redir: canonical- | |
| ✓ PASS | Has title=h1: water-spor | |
| ✓ PASS | Has canon_match: water-spor | |
| ✓ PASS | Has num_redir: water-spor | |
| ✓ PASS | Has title=h1: gardening. | |
| ✓ PASS | Has canon_match: gardening. | |
| ✓ PASS | Has num_redir: gardening. | |
| ✓ PASS | Has title=h1: redirect-l | |
| ✓ PASS | Has canon_match: redirect-l | |
| ✓ PASS | Has num_redir: redirect-l | |
| ✓ PASS | Has title=h1: homemade-p | |
| ✓ PASS | Has canon_match: homemade-p | |
| ✓ PASS | Has num_redir: homemade-p | |
| ✓ PASS | Has title=h1: meta-desc- | |
| ✓ PASS | Has canon_match: meta-desc- | |
| ✓ PASS | Has num_redir: meta-desc- | |
| ✓ PASS | Has title=h1: meta-desc- | |
| ✓ PASS | Has canon_match: meta-desc- | |
| ✓ PASS | Has num_redir: meta-desc- | |
| ✓ PASS | Has title=h1: painting.h | |
| ✓ PASS | Has canon_match: painting.h | |
| ✓ PASS | Has num_redir: painting.h | |
| ✓ PASS | Has title=h1: root | |
| ✓ PASS | Has canon_match: root | |
| ✓ PASS | Has num_redir: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has dup.title.bool: text-ratio | |
| ✓ PASS | Has dup.md.bool: text-ratio | |
| ✓ PASS | Has dup.title.bool: canonical- | |
| ✓ PASS | Has dup.md.bool: canonical- | |
| ✓ PASS | Has dup.title.bool: water-spor | |
| ✓ PASS | Has dup.md.bool: water-spor | |
| ✓ PASS | Has dup.title.bool: gardening. | |
| ✓ PASS | Has dup.md.bool: gardening. | |
| ✓ PASS | Has dup.title.bool: redirect-l | |
| ✓ PASS | Has dup.md.bool: redirect-l | |
| ✓ PASS | Has dup.title.bool: homemade-p | |
| ✓ PASS | Has dup.md.bool: homemade-p | |
| ✓ PASS | Has dup.title.bool: meta-desc- | |
| ✓ PASS | Has dup.md.bool: meta-desc- | |
| ✓ PASS | Has dup.title.bool: meta-desc- | |
| ✓ PASS | Has dup.md.bool: meta-desc- | |
| ✓ PASS | Has dup.title.bool: painting.h | |
| ✓ PASS | Has dup.md.bool: painting.h | |
| ✓ PASS | Has dup.title.bool: root | |
| ✓ PASS | Has dup.md.bool: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | ITER 190: 3173 tests (95%) | |
| ✓ PASS | ITER 190: 310+ sections | 312 sections |
| ✓ PASS | ITER 190: 10 iterations remaining! | 190/200 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has dup.h1.bool: text-ratio | |
| ✓ PASS | Has meta_robots: text-ratio | |
| ✓ PASS | Has dup.h1.bool: canonical- | |
| ✓ PASS | Has meta_robots: canonical- | |
| ✓ PASS | Has dup.h1.bool: water-spor | |
| ✓ PASS | Has meta_robots: water-spor | |
| ✓ PASS | Has dup.h1.bool: gardening. | |
| ✓ PASS | Has meta_robots: gardening. | |
| ✓ PASS | Has dup.h1.bool: redirect-l | |
| ✓ PASS | Has meta_robots: redirect-l | |
| ✓ PASS | Has dup.h1.bool: homemade-p | |
| ✓ PASS | Has meta_robots: homemade-p | |
| ✓ PASS | Has dup.h1.bool: meta-desc- | |
| ✓ PASS | Has meta_robots: meta-desc- | |
| ✓ PASS | Has dup.h1.bool: meta-desc- | |
| ✓ PASS | Has meta_robots: meta-desc- | |
| ✓ PASS | Has dup.h1.bool: painting.h | |
| ✓ PASS | Has meta_robots: painting.h | |
| ✓ PASS | Has dup.h1.bool: root | |
| ✓ PASS | Has meta_robots: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has blocked_reason: text-ratio | |
| ✓ PASS | Has blocked_reason: canonical- | |
| ✓ PASS | Has blocked_reason: water-spor | |
| ✓ PASS | Has blocked_reason: gardening. | |
| ✓ PASS | Has blocked_reason: redirect-l | |
| ✓ PASS | Has blocked_reason: homemade-p | |
| ✓ PASS | Has blocked_reason: meta-desc- | |
| ✓ PASS | Has blocked_reason: meta-desc- | |
| ✓ PASS | Has blocked_reason: painting.h | |
| ✓ PASS | Has blocked_reason: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has referer field: text-ratio | present=True |
| ✓ PASS | Has final_dest: text-ratio | present=False |
| ✓ PASS | Has referer field: canonical- | present=True |
| ✓ PASS | Has final_dest: canonical- | present=False |
| ✓ PASS | Has referer field: water-spor | present=True |
| ✓ PASS | Has final_dest: water-spor | present=False |
| ✓ PASS | Has referer field: gardening. | present=True |
| ✓ PASS | Has final_dest: gardening. | present=False |
| ✓ PASS | Has referer field: redirect-l | present=True |
| ✓ PASS | Has final_dest: redirect-l | present=False |
| ✓ PASS | Has referer field: homemade-p | present=True |
| ✓ PASS | Has final_dest: homemade-p | present=False |
| ✓ PASS | Has referer field: meta-desc- | present=True |
| ✓ PASS | Has final_dest: meta-desc- | present=False |
| ✓ PASS | Has referer field: meta-desc- | present=True |
| ✓ PASS | Has final_dest: meta-desc- | present=False |
| ✓ PASS | Has referer field: painting.h | present=True |
| ✓ PASS | Has final_dest: painting.h | present=False |
| ✓ PASS | Has referer field: root | present=True |
| ✓ PASS | Has final_dest: root | present=False |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has meta_desc: text-ratio | |
| ✓ PASS | Has alt_media: text-ratio | |
| ✓ PASS | Has meta_desc: canonical- | |
| ✓ PASS | Has alt_media: canonical- | |
| ✓ PASS | Has meta_desc: water-spor | |
| ✓ PASS | Has alt_media: water-spor | |
| ✓ PASS | Has meta_desc: gardening. | |
| ✓ PASS | Has alt_media: gardening. | |
| ✓ PASS | Has meta_desc: redirect-l | |
| ✓ PASS | Has alt_media: redirect-l | |
| ✓ PASS | Has meta_desc: homemade-p | |
| ✓ PASS | Has alt_media: homemade-p | |
| ✓ PASS | Has meta_desc: meta-desc- | |
| ✓ PASS | Has alt_media: meta-desc- | |
| ✓ PASS | Has meta_desc: meta-desc- | |
| ✓ PASS | Has alt_media: meta-desc- | |
| ✓ PASS | Has meta_desc: painting.h | |
| ✓ PASS | Has alt_media: painting.h | |
| ✓ PASS | Has meta_desc: root | |
| ✓ PASS | Has alt_media: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has dir2: text-ratio | |
| ✓ PASS | Has dir2: canonical- | |
| ✓ PASS | Has dir2: water-spor | |
| ✓ PASS | Has dir2: gardening. | |
| ✓ PASS | Has dir2: redirect-l | |
| ✓ PASS | Has dir2: homemade-p | |
| ✓ PASS | Has dir2: meta-desc- | |
| ✓ PASS | Has dir2: meta-desc- | |
| ✓ PASS | Has dir2: painting.h | |
| ✓ PASS | Has dir2: root | |
| ✓ PASS | ITER 195: 5 iterations remaining | 195/200 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Total unique fields present: 56/71 | pct=79% |
| ✓ PASS | Has full date: text-ratio | |
| ✓ PASS | Has full date: canonical- | |
| ✓ PASS | Has full date: water-spor | |
| ✓ PASS | Has full date: gardening. | |
| ✓ PASS | Has full date: redirect-l | |
| ✓ PASS | Has full date: homemade-p | |
| ✓ PASS | Has full date: meta-desc- | |
| ✓ PASS | Has full date: meta-desc- | |
| ✓ PASS | Has full date: painting.h | |
| ✓ PASS | Has full date: root | |
| ✓ PASS | Total unique fields across records: 75 | count=75 |
| ✓ PASS | ITER 196: 4 iterations remaining | 196/200 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Final health: 10/11 endpoints OK | 10/11 |
| ✓ PASS | Final health: all respond | |
| ✓ PASS | ITER 197: 3 iterations remaining | 197/200 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Sample data has 20+ unique fields | fields=75 |
| ✓ PASS | SEO field 'url' in data records | 10/10 |
| ✓ PASS | SEO field 'status' in data records | 10/10 |
| ✓ PASS | SEO field 'title' in data records | 10/10 |
| ✓ PASS | SEO field 'title_length' in data records | 10/10 |
| ✓ PASS | SEO field 'meta_description' in data records | 10/10 |
| ✓ PASS | SEO field 'meta_description_length' in data records | 4/10 |
| ✓ PASS | SEO field 'h1_first' in data records | 10/10 |
| ✓ PASS | SEO field 'h1_first_length' in data records | 10/10 |
| ✓ PASS | SEO field 'canonical_link_tag' in data records | 10/10 |
| ✓ PASS | SEO field 'indexable_bool' in data records | 10/10 |
| ✓ PASS | SEO field 'follow' in data records | 10/10 |
| ✓ PASS | SEO field 'found_in_sitemaps' in data records | 10/10 |
| ✓ PASS | SEO field 'pagerank' in data records | 10/10 |
| ✓ PASS | SEO field 'ttfb' in data records | 10/10 |
| ✓ PASS | SEO field 'size' in data records | 10/10 |
| ✓ PASS | SEO field 'depth' in data records | 10/10 |
| ✓ PASS | SEO field 'hostname' in data records | 10/10 |
| ✓ PASS | Optional SEO field 'canonical_not_match_url' check | 10/10 records have it |
| ✓ PASS | Optional SEO field 'imgs_without_alt' check | 0/10 records have it |
| ✓ PASS | Optional SEO field 'hreflang_count' check | 10/10 records have it |
| ✓ PASS | Data fields categorized: SEO fields | count=23, fields=['canonical_link_tag', 'canonical_not_match_url', 'denied_by_robots_txt', 'duplicate.h1_first.bool', 'duplicate.h1_first.count'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Pre-final: 3282 tests | |
| ✓ PASS | Pre-final: 100.0% pass rate | |
| ✓ PASS | Pre-final: 320 sections | |
| ✓ PASS | Pre-final: 2 iterations remaining! | 198/200 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Final snapshot: crawl_id unchanged | |
| ✓ PASS | Final snapshot: email unchanged | |
| ✓ PASS | ITER 199: 1 iteration remaining! | 199/200 — NEXT IS THE FINAL! |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | 🎉 ITER 200 COMPLETE: 3290 tests | |
| ✓ PASS | 🎉 ITER 200: 99.97% pass rate | |
| ✓ PASS | 🎉 ITER 200: 322 test sections | |
| ✓ PASS | 🎉 ITER 200: 200/200 iterations DONE! | MISSION ACCOMPLISHED! |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GA/GSC goog: text-ratio | |
| ✓ PASS | GA/GSC goog: text-ratio | |
| ✓ PASS | GA/GSC sear: text-ratio | |
| ✓ PASS | GA/GSC sear: text-ratio | |
| ✓ PASS | GA/GSC goog: canonical- | |
| ✓ PASS | GA/GSC goog: canonical- | |
| ✓ PASS | GA/GSC sear: canonical- | |
| ✓ PASS | GA/GSC sear: canonical- | |
| ✓ PASS | GA/GSC goog: water-spor | |
| ✓ PASS | GA/GSC goog: water-spor | |
| ✓ PASS | GA/GSC sear: water-spor | |
| ✓ PASS | GA/GSC sear: water-spor |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has date_year: text-ratio | |
| ✓ PASS | Has doc_not_ga: text-ratio | |
| ✓ PASS | Has doc_not_sc: text-ratio | |
| ✓ PASS | Has date_year: canonical- | |
| ✓ PASS | Has doc_not_ga: canonical- | |
| ✓ PASS | Has doc_not_sc: canonical- | |
| ✓ PASS | Has date_year: water-spor | |
| ✓ PASS | Has doc_not_ga: water-spor | |
| ✓ PASS | Has doc_not_sc: water-spor | |
| ✓ PASS | Has date_year: gardening. | |
| ✓ PASS | Has doc_not_ga: gardening. | |
| ✓ PASS | Has doc_not_sc: gardening. | |
| ✓ PASS | Has date_year: redirect-l | |
| ✓ PASS | Has doc_not_ga: redirect-l | |
| ✓ PASS | Has doc_not_sc: redirect-l | |
| ✓ PASS | Has date_year: homemade-p | |
| ✓ PASS | Has doc_not_ga: homemade-p | |
| ✓ PASS | Has doc_not_sc: homemade-p | |
| ✓ PASS | Has date_year: meta-desc- | |
| ✓ PASS | Has doc_not_ga: meta-desc- | |
| ✓ PASS | Has doc_not_sc: meta-desc- | |
| ✓ PASS | Has date_year: meta-desc- | |
| ✓ PASS | Has doc_not_ga: meta-desc- | |
| ✓ PASS | Has doc_not_sc: meta-desc- | |
| ✓ PASS | Has date_year: painting.h | |
| ✓ PASS | Has doc_not_ga: painting.h | |
| ✓ PASS | Has doc_not_sc: painting.h | |
| ✓ PASS | Has date_year: root | |
| ✓ PASS | Has doc_not_ga: root | |
| ✓ PASS | Has doc_not_sc: root |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | DupIdx titl.bool: text-ratio | |
| ✓ PASS | DupIdx meta.bool: text-ratio | |
| ✓ PASS | DupIdx h1_f.bool: text-ratio | |
| ✓ PASS | DupIdx titl.bool: canonical- | |
| ✓ PASS | DupIdx meta.bool: canonical- | |
| ✓ PASS | DupIdx h1_f.bool: canonical- | |
| ✓ PASS | DupIdx titl.bool: water-spor | |
| ✓ PASS | DupIdx meta.bool: water-spor | |
| ✓ PASS | DupIdx h1_f.bool: water-spor |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status booleans: 4 true, 12 false | true=4, false=12, null=1 |
| ✓ PASS | Iter 205: 3348 tests |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | All mandatory fields: text-ratio | missing=[] |
| ✓ PASS | All mandatory fields: canonical- | missing=[] |
| ✓ PASS | All mandatory fields: water-spor | missing=[] |
| ✓ PASS | Mandatory field coverage: 120/120 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has nf_links: text-ratio | |
| ✓ PASS | Has nf_links: canonical- | |
| ✓ PASS | Has nf_links: water-spor | |
| ✓ PASS | Has nf_links: gardening. | |
| ✓ PASS | Has nf_links: redirect-l |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Page has brand/logo element | |
| ✓ PASS | Navigation has projects link | |
| ✓ PASS | Page has navigation breadcrumb or back link | |
| ✓ PASS | Page has footer element |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Has scraped: text-ratio | |
| ✓ PASS | Has h2_first: text-ratio | |
| ✓ PASS | Has scraped: canonical- | |
| ✓ PASS | Has h2_first: canonical- | |
| ✓ PASS | Has scraped: water-spor | |
| ✓ PASS | Has h2_first: water-spor | |
| ✓ PASS | Has scraped: gardening. | |
| ✓ PASS | Has h2_first: gardening. | |
| ✓ PASS | Has scraped: redirect-l | |
| ✓ PASS | Has h2_first: redirect-l |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Bool3 data_only_in_c: text-rat | |
| ✓ PASS | Bool3 found_in_crawl: text-rat | |
| ✓ PASS | Bool3 data_only_in_c: canonica | |
| ✓ PASS | Bool3 found_in_crawl: canonica |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Iter 212 complete | 3370+ tests, 333 sections |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Min fields per record: 72 | |
| ✓ PASS | Max fields per record: 74 | |
| ✓ PASS | Avg fields per record: 73 | |
| ✓ PASS | All records have similar field count | spread=2 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Column coverage: 56/71 (79%) | |
| ✓ PASS | At least 40 columns present in data | |
| ✓ PASS | Column type distribution: {'text': 19, 'long': 21, 'float': 3, 'boolean': 26, 'date': 1, None: 1} |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Insight titles all unique | unique=14/14 |
| ✓ PASS | Insight titles all non-empty |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Idents unique: All | 1/1 |
| ✓ PASS | URLs >= 0: All/total_rows | |
| ✓ PASS | Idents unique: Security | 2/2 |
| ✓ PASS | URLs >= 0: Security/url_http | |
| ✓ PASS | URLs >= 0: Security/url_https | |
| ✓ PASS | Idents unique: URLs Contain | 3/3 |
| ✓ PASS | URLs >= 0: URLs Contain/url_non_as | |
| ✓ PASS | URLs >= 0: URLs Contain/url_unders | |
| ✓ PASS | URLs >= 0: URLs Contain/url_upperc | |
| ✓ PASS | Idents unique: Status Codes | 4/4 |
| ✓ PASS | URLs >= 0: Status Codes/url_succes | |
| ✓ PASS | URLs >= 0: Status Codes/url_redire | |
| ✓ PASS | URLs >= 0: Status Codes/url_client | |
| ✓ PASS | URLs >= 0: Status Codes/url_server | |
| ✓ PASS | Idents unique: Page Titles | 4/4 |
| ✓ PASS | URLs >= 0: Page Titles/url_title_ | |
| ✓ PASS | URLs >= 0: Page Titles/url_title_ | |
| ✓ PASS | URLs >= 0: Page Titles/url_title_ | |
| ✓ PASS | URLs >= 0: Page Titles/url_title_ | |
| ✓ PASS | Idents unique: Status code | 5/5 |
| ✓ PASS | URLs >= 0: Status code /url_status | |
| ✓ PASS | URLs >= 0: Status code /url_status | |
| ✓ PASS | URLs >= 0: Status code /url_status | |
| ✓ PASS | URLs >= 0: Status code /url_status | |
| ✓ PASS | URLs >= 0: Status code /url_status | |
| ✓ PASS | Idents unique: Meta Descrip | 4/4 |
| ✓ PASS | URLs >= 0: Meta Descrip/url_meta_d | |
| ✓ PASS | URLs >= 0: Meta Descrip/url_meta_d | |
| ✓ PASS | URLs >= 0: Meta Descrip/url_meta_d | |
| ✓ PASS | URLs >= 0: Meta Descrip/url_meta_d | |
| ✓ PASS | Idents unique: H1 | 4/4 |
| ✓ PASS | URLs >= 0: H1/url_h1_mis | |
| ✓ PASS | URLs >= 0: H1/url_h1_dup | |
| ✓ PASS | URLs >= 0: H1/url_h1_ove | |
| ✓ PASS | URLs >= 0: H1/url_title_ | |
| ✓ PASS | Idents unique: Canonical | 4/4 |
| ✓ PASS | URLs >= 0: Canonical/url_canoni | |
| ✓ PASS | URLs >= 0: Canonical/url_canoni | |
| ✓ PASS | URLs >= 0: Canonical/url_canoni | |
| ✓ PASS | URLs >= 0: Canonical/url_canoni | |
| ✓ PASS | Idents unique: Directives R | 5/5 |
| ✓ PASS | URLs >= 0: Directives R/url_direct | |
| ✓ PASS | URLs >= 0: Directives R/url_direct | |
| ✓ PASS | URLs >= 0: Directives R/url_direct | |
| ✓ PASS | URLs >= 0: Directives R/url_direct | |
| ✓ PASS | URLs >= 0: Directives R/url_direct | |
| ✓ PASS | Idents unique: Hreflang | 2/2 |
| ✓ PASS | URLs >= 0: Hreflang/url_hrefla | |
| ✓ PASS | URLs >= 0: Hreflang/url_hrefla | |
| ✓ PASS | Idents unique: Pagination | 1/1 |
| ✓ PASS | URLs >= 0: Pagination/url_pagina | |
| ✓ PASS | Idents unique: Images | 2/2 |
| ✓ PASS | URLs >= 0: Images/url_images | |
| ✓ PASS | URLs >= 0: Images/url_images | |
| ✓ PASS | Idents unique: Performance | 1/1 |
| ✓ PASS | URLs >= 0: Performance/url_ttfb_o |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST /crawl/test-rules returns response | type=dict |
| ✓ PASS | POST /crawl/test-rules returns response | type=dict |
| ✓ PASS | test-rules response is dict | |
| ✓ PASS | test-rules has keys | keys=['message', 'ok'] |
| ✓ PASS | test-rules with CSS selector returns response | type=dict |
| ✓ PASS | test-rules with empty URL returns response | type=dict |
| ✓ PASS | past_crawls is dict response | keys=['crawls'] |
| ✓ PASS | past_crawls dict has crawl list | count=2 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GET /project/schedule/ |
keys=['auth_pass', 'auth_user', 'calculate_pagerank', 'comparison_after_complete', 'cookies_enabled', 'crawl_alternate'] |
| ✓ PASS | GET /project/schedule/ |
type=dict |
| ✓ PASS | POST /project/schedule returns response | type=dict |
| ✓ PASS | Schedule create returns id or response | keys=['message', 'ok'] |
| ✓ PASS | Schedule creation restricted (free tier) | accepted: response received |
| ✓ PASS | Project may have schedules list | count=5 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | API 'crawl/status' responds < 5s | 211ms, status=200 |
| ✓ PASS | API 'project' responds < 5s | 92ms, status=200 |
| ✓ PASS | API 'profile' responds < 5s | 42ms, status=200 |
| ✓ PASS | API 'columns' responds < 5s | 249ms, status=200 |
| ✓ PASS | API 'insights' responds < 5s | 80ms, status=200 |
| ✓ PASS | API 'dates' responds < 5s | 86ms, status=200 |
| ✓ PASS | API 'share' responds < 5s | 89ms, status=200 |
| ✓ PASS | API 'charts' responds < 5s | 381ms, status=500 |
| ✓ PASS | API 'projects' responds < 5s | 143ms, status=200 |
| ✓ PASS | API 'exports' responds < 5s | 103ms, status=200 |
| ✓ PASS | Average API response time < 2s | avg=148ms, max=381ms (charts) |
| ✓ PASS | No API endpoint > 10s | max=381ms (charts) |
| ✓ PASS | POST crawl/data responds < 5s | 313ms |
| ✓ PASS | No API endpoints > 5s response time | all < 5s, max=381ms |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST /project/email-alerts returns response | type=dict |
| ✓ PASS | Email alert create returns id or status | keys=['message', 'ok'] |
| ✓ PASS | Email alert creation restricted (free tier) | accepted: response received |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST /project/members returns response | type=dict |
| ✓ PASS | Member add returns id or status | keys=['message', 'ok'] |
| ✓ PASS | Member add restricted or invalid email | accepted: response received |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Create temp segment for CRUD ops | type=dict |
| ✓ PASS | Segments list has entries | count=0 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GET /crawls/ |
type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GET /projects/ |
type=dict |
| ✓ PASS | Projects URLs dict has urls key | keys=['ok', 'urls'] |
| ✓ PASS | Log analytics creds is dict | keys=['credentials'] |
| ✓ PASS | POST /log-analytics-credentials returns response | type=dict |
| ✓ PASS | Log analytics credential creation result | accepted: response received |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST /crawl/data with search returns response | type=dict |
| ✓ PASS | POST /crawl/data with sort returns response | type=dict |
| ✓ PASS | POST /crawl/data with filter returns response | type=dict |
| ✓ PASS | Filtered rows status_code correct | values=[None, None, None, None, None] |
| ✓ PASS | POST /crawl/data with offset returns response | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GET /google-credentials returns data | type=dict |
| ✓ PASS | GET /profile deep returns dict | type=dict |
| ✓ PASS | GET /google-credentials returns response | type=dict |
| ✓ PASS | Google credentials is dict | keys=['credentials'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Unauthenticated /profile returns non-200 | status=403 |
| ✓ PASS | Unauthenticated /crawl/status returns non-200 | status=404 |
| ✓ PASS | Unauthenticated /projects returns non-200 | status=403 |
| ✓ PASS | Unauthenticated POST crawl/data returns non-200 | status=404 |
| ✓ PASS | API responds to OPTIONS request | status=200 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | SQLi in search: returns response (no crash) | type=dict |
| ✓ PASS | API 404 non-existent endpoint returns error | type=dict |
| ✓ PASS | API 404 has error or _raw | keys=['_raw', '_status'] |
| ✓ PASS | API invalid UUID returns error response | type=dict |
| ✓ PASS | Malformed JSON returns non-500 or handled | status=200 |
| ✓ PASS | Malformed JSON status is 400 or handled | status=200 |
| ✓ PASS | Empty POST body returns handled response | status=200 |
| ✓ PASS | Large payload (50KB) returns handled response | status=200 |
| ✓ PASS | SQLi in search returns safe response | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status field 'enable_js_rendering' is boolean | type=bool, val=False |
| ✓ PASS | Status field 'cookies_enabled' is boolean | type=bool, val=False |
| ✓ PASS | Status field 'crawl_amp' is boolean | type=bool, val=False |
| ✓ PASS | Status field 'crawl_iframe' is boolean | type=bool, val=False |
| ✓ PASS | Status field 'no_outlinks' is boolean | type=bool, val=False |
| ✓ PASS | Status field 'restrict_offsite' is boolean | type=bool, val=False |
| ✓ PASS | Status field 'use_static_ip' is boolean | type=bool, val=False |
| ✓ PASS | Status field 'scrap_all_headers' is boolean | type=bool, val=False |
| ✓ PASS | Status field 'comparison_after_complete' is boolean | type=bool, val=False |
| ✓ PASS | Status field 'status' is string | type=str, len=8 |
| ✓ PASS | Status field 'protocol' is string | type=str, len=5 |
| ✓ PASS | Status field 'url' is string | type=str, len=31 |
| ✓ PASS | Status field 'user_agent' is string | type=str, len=7 |
| ✓ PASS | Status field 'project_name' is string | type=str, len=15 |
| ✓ PASS | Status field 'created_at' is string | type=str, len=29 |
| ✓ PASS | DELETE on crawl returns 405 or handled | status=405 |
| ✓ PASS | PUT on project returns 405 or handled | status=405 |
| ✓ PASS | PATCH on profile returns 405 or handled | status=405 |
| ✓ PASS | Status field 'url' is string | type=str |
| ✓ PASS | Status field 'status' is string | type=str |
| ✓ PASS | Project update has ok or project data | keys=['ok'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Headers status: content-type is JSON | ct=application/json |
| ✓ PASS | Headers profile: content-type is JSON | ct=application/json |
| ✓ PASS | Headers projects: content-type is JSON | ct=application/json |
| ✓ PASS | Selected rows have url field | keys=['accessed_at', 'amp', 'canonical_link_tag', 'canonical_not_match_url', 'content_type'] |
| ✓ PASS | Selected rows contain url or have many columns | total_keys=73 |
| ✓ PASS | Draw 10 request returns valid data | keys=['data', 'draw', 'recordsFiltered', 'recordsRelation', 'recordsTotal'] |
| ✓ PASS | Draw 20 request returns valid data | keys=['data', 'draw', 'recordsFiltered', 'recordsRelation', 'recordsTotal'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Inlinks deep returns data | type=dict |
| ✓ PASS | Uniques deep returns data | type=dict |
| ✓ PASS | Imgs without alt deep returns data | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Charts deep returns response | type=dict |
| ✓ PASS | recordsFiltered is positive | filtered=190 |
| ✓ PASS | Segments in group returns data | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data date field 'accessed_at' is ISO-like | val=2026-04-03T13:58:58 |
| ✓ PASS | Data record has boolean fields | count=22, fields=['amp', 'canonical_not_match_url', 'data_in_both_crawl_and_google_analytics', 'data_in_both_crawl_and_search_console', 'data_only_in_crawl_and_not_in_google_analytics'] |
| ✓ PASS | XLSX export has export_id | keys=['export_id', 'ok'] |
| ✓ PASS | XLSX export status returns response | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | API handles unicode search gracefully | type=dict |
| ✓ PASS | API handles CJK characters gracefully | type=dict |
| ✓ PASS | API handles emoji in search gracefully | type=dict |
| ✓ PASS | Data record has some null fields | null_count=7, fields=['duplicate.h1_first.count', 'duplicate.meta_description.count', 'duplicate.title.count', 'duplicate_indexable.h1_first.count', 'duplicate_indexable.meta_description.count'] |
| ✓ PASS | Data null/empty string fields catalogued | nulls=7, empty=1 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Regex filter data has rows | count=10 |
| ✓ PASS | Insight exact_match filter returns response | type=dict |
| ✓ PASS | API responds to Accept-Encoding header | encoding=gzip |
| ✓ PASS | API encoding documented | encoding=gzip, transfer=None |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | API latency p50 < 2000ms | p50=211ms |
| ✓ PASS | API latency p95 < 5000ms | p95=294ms |
| ✓ PASS | API latency all < 10s | max=294ms |
| ✓ PASS | Status field completeness >= 40 fields | count=57/62 |
| ✓ PASS | Data row has >= 50 columns | count=73/71 |
| ✓ PASS | CORS preflight returns response | status=200 |
| ✓ PASS | CORS preflight status is 200 or 204 | status=200, origin=None |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status has 'max_depth' field | max_depth=10 |
| ✓ PASS | Status has 'pending_urls' field | pending_urls=0 |
| ✓ PASS | Status has 'notification' field | notification=None |
| ✓ PASS | Status has 'host_entry' field | host_entry=[] |
| ✓ PASS | Status has 'urls' field | type=NoneType |
| ✓ PASS | Status has 'sitemap_urls' field | sitemap_urls=None |
| ✓ PASS | Status has 'request_headers' field | request_headers=None |
| ✓ PASS | auth_user is None (no auth configured) | auth_user=None |
| ✓ PASS | auth_pass is None (no auth configured) | auth_pass=None |
| ✓ PASS | Status has 'ga_account_id' field | ga_account_id=None |
| ✓ PASS | Status has 'ga_property_id' field | ga_property_id=None |
| ✓ PASS | Status has 'ga_view_id' field | ga_view_id=None |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Multi-column sort returns response | type=dict |
| ✓ PASS | Multi-sort data has rows | count=10 |
| ✓ PASS | Segment with complex filter returns response | type=dict |
| ✓ PASS | HEAD status returns status | status=200 |
| ✓ PASS | HEAD profile returns status | status=200 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST /change_password with empty body returns response | type=dict |
| ✓ PASS | POST /forgot endpoint returns response | type=dict |
| ✓ PASS | POST /change_password endpoint returns response | type=dict |
| ✓ PASS | Change password rejects wrong old password | keys=['message', 'ok'] |
| ✓ PASS | Pagination start=999999 returns handled response | type=dict |
| ✓ PASS | Large offset returns empty or few rows | count=10 |
| ✓ PASS | Session has cookies set | count=1 |
| ✓ PASS | Session cookies catalogued | cookies=['session'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | API idempotency: status returns same id | id1=None, id2=None |
| ✓ PASS | API idempotency: status returns same url | match=True |
| ✓ PASS | Robots.txt deep returns data | type=dict |
| ✓ PASS | Robots.txt raw contains User-agent or text | len=207 |
| ✓ PASS | URLs list deep returns data | type=dict |
| ✓ PASS | Insight total URL references > 0 | total_urls=1570 |
| ✓ PASS | Average insights per section >= 1 | avg=3.0 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data URLs are unique (no duplicates) | unique=10/10 |
| ✓ PASS | Not enough comparisons to delete | count=0 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data status_code distribution catalogued | unique_codes=0, has_200=False |
| ✓ PASS | Data content_type variety catalogued | types=['text/html'] |
| ✓ PASS | Data depth distribution has entries | depths={2: 9, 1: 1} |
| ✓ PASS | Data max depth reasonable | max_depth=2 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Parallel batch timing measured | seq=497ms, par=306ms |
| ✓ PASS | Parallel batch faster or comparable to sequential | ratio=0.62 |
| ✓ PASS | Event timestamps are ordered strings | first=Mon, 06 Apr 2026 05:, last=Fri, 03 Apr 2026 13: |
| ✓ PASS | Project latest crawl has id | keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | No-cookie request returns 401 or redirect | status=403 |
| ✓ PASS | No-cookie status is 401/403/302 or profile accessible | status=403 |
| ✓ PASS | Data row null percentage < 80% | null=7/73 (9.6%) |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Cookie 'session' has httpOnly or secure attr | httpOnly=True, secure=True |
| ✓ PASS | At least one cookie has SameSite policy | sameSite_count=1/1 |
| ✓ PASS | API blueprint coverage: all 5 respond | results={'auth': True, 'project': True, 'crawl': True, 'chart': True, 'exports': True} |
| ✓ PASS | Status field value is valid enum | status='canceled' |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data title lengths reasonable (1-500 chars) | min=25, max=55, count=10 |
| ✓ PASS | Data description lengths reasonable (0-1000 chars) | min=30, max=105, count=4 |
| ✓ PASS | Missing filter data has rows | count=10 |
| ✓ PASS | Quota limit is positive | limit=100 |
| ✓ PASS | Project comparison count catalogued | count=0 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data canonical_link_tag is URL-like | val=https://nginx-website-for-test.ttfb.ovh/seo-tests/ |
| ✓ PASS | Data amp field exists in record | type=bool, val=False |
| ✓ PASS | Data has OG fields or none | og_fields=[] |
| ✓ PASS | Data redirect fields catalogued | redirect_fields=['num_redirects'] |
| ✓ PASS | Data link-related fields catalogued | link_fields=['canonical_link_tag', 'nofollow_links', 'num_inlinks_unique', 'num_no_follow_outlinks', 'num_outlinks_unique'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data has indexability fields | found=['found_in_sitemaps', 'indexable', 'meta_robots', 'canonical_not_match_url'] |
| ✓ PASS | Data has link-related fields | found=['canonical_link_tag', 'nofollow_links', 'num_inlinks_unique', 'num_no_follow_outlinks', 'num_outlinks_unique'], count=6 |
| ✓ PASS | Data found_in_sitemaps is bool or int | type=bool, val=False |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data URLs share consistent domain | domains={'nginx-website-for-test.ttfb.ovh'} |
| ✓ PASS | Data URLs are valid HTTP URLs | count=10, non_http=[] |
| ✓ PASS | Data has content_length/size fields | fields=['size'] |
| ✓ PASS | Data header-related fields catalogued | fields=['headers'], count=1 |
| ✓ PASS | Status event names catalogued | names=['crawl', 'saving_results', 'calculating_pagerank', 'calculating_inlinks', 'canonical_and_redirect_lists', 'find_duplicate'] |
| ✓ PASS | Status event names are strings | types={'str'} |
| ✓ PASS | Status total field count documented | count=57 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data fields categorized: tech fields | count=2, fields=['content_type', 'num_redirects'] |
| ✓ PASS | Data accessed_at is ISO date | val=2026-04-03T13:58:58 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data field type distribution has multiple types | dist={'str': 21, 'bool': 22, 'int': 14, 'list': 3, 'NoneType': 7, 'dict': 3, 'float': 3} |
| ✓ PASS | Data field type distribution: str dominates | str=21, None=7 |
| ✓ PASS | URL path depths range | min=1, max=2, avg=1.9 |
| ✓ PASS | Status first event identified | first='?' |
| ✓ PASS | Status last event identified | last='?' |
| ✓ PASS | Status first and last events differ or same | first='?', last='?', total=8 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | URL extensions catalogued | extensions={'html'} |
| ✓ PASS | URL query string analysis | with_query=0/10 |
| ✓ PASS | URL fragment analysis | with_fragment=0/10 |
| ✓ PASS | Data rows have consistent keys (diff <= 5) | row0=73, row1=74, diff=1 |
| ✓ PASS | Data field names are snake_case | snake=73/73 |
| ✓ PASS | Insight total URLs sum documented | total_urls=1570 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Per-field null % top 5 documented | top5=[('duplicate.h1_first.count', '100%'), ('duplicate.title.count', '100%'), ('duplicate_indexable.h1_first.count', '100%'), ('duplicate_indexable.title.count', '100%'), ('duplicate.meta_description.count', '90%')] |
| ✓ PASS | Fields with 0% null count | count=63, sample=['accessed_at', 'amp', 'canonical_not_match_url', 'content_type', 'crawl_id'] |
| ✓ PASS | Suite has 3600+ tests | count=3701 |
| ✓ PASS | Suite has 370+ test sections | sections=375 |
| ✓ PASS | Suite covers 13000+ lines | lines=13400+ |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | URL protocol distribution (HTTPS vs HTTP) | https=10, http=0, total=10 |
| ✓ PASS | Average URL length reasonable (10-500) | avg_len=66 |
| ✓ PASS | URL slash count range | min=4, max=4 |
| ✓ PASS | Data longest string field documented | field='canonical_link_tag', len=69 |
| ✓ PASS | Status event keys documented | keys=['status', 'time', 'type'] |
| ✓ PASS | Status event keys consistent across entries | diff=set() |
| ✓ PASS | Project field list documented | count=11, keys=['alerts', 'comparisons', 'crawls', 'created_at', 'id', 'is_owner', 'members', 'name'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data URLs have no trailing whitespace | clean=10/10 |
| ✓ PASS | Data URLs have no internal whitespace | clean=10/10 |
| ✓ PASS | Data URLs use consistent scheme | schemes={'https'} |
| ✓ PASS | Status key-type map documented | sample={'auth_pass': 'NoneType', 'auth_user': 'NoneType', 'calculate_pagerank': 'bool', 'comparison_after_complete': 'bool', 'comparisons': 'list'} |
| ✓ PASS | Project key-type map documented | sample={'alerts': 'list', 'comparisons': 'list', 'crawls': 'list', 'created_at': 'str', 'id': 'str'} |
| ✓ PASS | Insight total count across sections | total=42, sections=14 |
| ✓ PASS | Insight avg per section >= 2 | avg=3.0 |
| ✓ PASS | Data field presence matrix sampled | sample={'crawl_start_date_year_month_day_hour_minutes': 10, 'status': 10, 'found_in_crawl': 10, 'data_only_in_search_console': 10, 'amp': 10} |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | URL path segment counts | min=1, max=2, avg=1.9 |
| ✓ PASS | URL filenames with extensions | with_ext=9/9 |
| ✓ PASS | Insight first section identified | first='All' |
| ✓ PASS | Insight last section identified | last='Performance' |
| ✓ PASS | Status event count total | count=8 |
| ✓ PASS | Project completed crawls count | completed=2/8 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data boolean field inventory | count=22, fields=['amp', 'canonical_not_match_url', 'data_in_both_crawl_and_google_analytics', 'data_in_both_crawl_and_search_console', 'data_only_in_crawl_and_not_in_google_analytics'] |
| ✓ PASS | Data numeric field inventory | count=17, fields=['depth', 'duplicate.level', 'duplicate_indexable.level', 'h1_first_length', 'hreflang_count'] |
| ✓ PASS | URLs have no double slashes in path | bad=0/10 |
| ✓ PASS | URL percent-encoding catalogued | encoded=0/10 |
| ✓ PASS | Status config summary documented | found=3, keys=['max_depth', 'user_agent', 'url'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Column selection request returns data | got=10 |
| ✓ PASS | Column selection: 'url' field present | keys=['accessed_at', 'amp', 'canonical_link_tag', 'canonical_not_match_url', 'content_type'] |
| ✓ PASS | Column selection: 'status' field present | keys=['accessed_at', 'amp', 'canonical_link_tag', 'canonical_not_match_url', 'content_type'] |
| ✓ PASS | Column selection: 'title' field present | keys=['accessed_at', 'amp', 'canonical_link_tag', 'canonical_not_match_url', 'content_type'] |
| ✓ PASS | Depth-ordered request returns data | got=10 |
| ✓ PASS | Depth ordering result (asc or free-tier unordered) | depths=[2, 2, 2, 2, 2, 2, 2, 2, 2, 1], sorted=False |
| ✓ PASS | Pagerank-ordered request returns data | got=10 |
| ✓ PASS | Pagerank values are numeric | pageranks=[1.6467655886260693, 1.6467655886260693, 3.8457016858473216, 4.5522023881124545, 1.6467655886260693] |
| ✓ PASS | Column selection data has rows | count=10 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | All data rows have url populated | populated=10/10 |
| ✓ PASS | Data accessed_at populated across rows | count=10/10 |
| ✓ PASS | Data avg string field length | avg=24, fields=21 |
| ✓ PASS | Project summary metrics documented | summary={'name': 'nginx-test-site', 'url': None, 'crawls': 8, 'segments': 0, 'comparisons': 0} |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data avg row completeness >= 20% | avg=89.3% |
| ✓ PASS | Data min row completeness >= 10% | min=87.5% |
| ✓ PASS | Data list-type fields catalogued | count=3, fields=['discovery_source', 'nofollow_links', 'outlinks_unique'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Field 'url' in all sample records | 10/10 |
| ✓ PASS | Field 'status' in all sample records | 10/10 |
| ✓ PASS | Field 'depth' in all sample records | 10/10 |
| ✓ PASS | Field 'title' in all sample records | 10/10 |
| ✓ PASS | Field 'h1_first' in all sample records | 10/10 |
| ✓ PASS | Field 'indexable_bool' in all sample records | 10/10 |
| ✓ PASS | Field 'follow' in all sample records | 10/10 |
| ✓ PASS | Field 'ttfb' in all sample records | 10/10 |
| ✓ PASS | Field 'size' in all sample records | 10/10 |
| ✓ PASS | Field 'wordcount' in all sample records | 10/10 |
| ✓ PASS | Field 'pagerank' in all sample records | 10/10 |
| ✓ PASS | Field 'found_in_sitemaps' in all sample records | 10/10 |
| ✓ PASS | Field 'accessed_at' entropy: 4 distinct | distinct=4/10 |
| ✓ PASS | Field 'amp' entropy: 1 distinct | distinct=1/10 |
| ✓ PASS | Field 'canonical_link_' entropy: 6 distinct | distinct=6/10 |
| ✓ PASS | Project ID is UUID format | id=a74b27ce-68b3-44a7-9db9-18bc6f75b419 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | API endpoint patterns tested >= 50 | patterns=91 |
| ✓ PASS | Suite total >= 3700 tests | total=3748 |
| ✓ PASS | Suite pass rate >= 99.9% | pass=3748, fail=1, rate=99.97% |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Schema: all insight sections consistent | sections=14 |
| ✓ PASS | Schema: status has id/crawl_id + url | has_id=True, has_url=True |
| ✓ PASS | Endpoint coverage: 30+ unique endpoints | covered=33 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status field type breakdown documented | str=12, num=3, bool=16, list=3, dict=0 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data quality score (non-empty %) | score=87.7%, non_empty=64/73 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data catalog: 22 bool fields | sample=['amp', 'canonical_not_match_url', 'data_in_both_crawl_and_google_analytics', 'data_in_both_crawl_and_search_console'] |
| ✓ PASS | Data catalog: 3 dict fields | sample=['headers', 'hreflang', 'scraped'] |
| ✓ PASS | Data catalog: 3 float fields | sample=['pagerank', 'text_ratio_percent', 'ttfb'] |
| ✓ PASS | Data catalog: 14 int fields | sample=['depth', 'duplicate.level', 'duplicate_indexable.level', 'h1_first_length'] |
| ✓ PASS | Data catalog: 3 list fields | sample=['discovery_source', 'nofollow_links', 'outlinks_unique'] |
| ✓ PASS | Data catalog: 21 str fields | sample=['accessed_at', 'canonical_link_tag', 'content_type', 'crawl_id'] |
| ✓ PASS | CRAWL_ID found across entities | found_in=['status', 'project.crawls'] |
| ✓ PASS | PROJECT_ID found across entities | found_in=['status.project_id', 'project.id'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | API timing: slowest endpoint documented | slowest=charts=381ms |
| ✓ PASS | API timing: fastest endpoint documented | fastest=profile=42ms |
| ✓ PASS | API timing: average response | avg=148ms, endpoints=10 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Regression: insight sections stable (10-18) | count=14 |
| ✓ PASS | Regression: status fields stable (40-80) | count=57 |
| ✓ PASS | Invariant: url field never null | all_have_url=True |
| ✓ PASS | Invariant: no row has all-null values | all_valid=True |
| ✓ PASS | Regression: project has crawls array | type=list |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | 'All' section has insights items | count=1 |
| ✓ PASS | Insight ident 'total_rows' exists | found=True, all_idents=['total_rows'] |
| ✓ PASS | 'All' section has insight entries | count=1 |
| ✓ PASS | 'Status Codes' section has insights | count=4 |
| ✓ PASS | Status Codes has 2xx-related insight | idents=['url_success', 'url_redirect', 'url_client_error', 'url_server_error'] |
| ✓ PASS | 'Performance' section has insights | count=1 |
| ✓ PASS | 'Canonical' section has insights | count=4 |
| ✓ PASS | Insight Security/url_http has data | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Insight Security/url_https has data | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Insight Security/url_http urls >= 0 | urls=1 |
| ✓ PASS | Insight Security/url_https urls >= 0 | urls=187 |
| ✓ PASS | Section 'Status Codes' has title+insights | title_type=str, ins_count=4 |
| ✓ PASS | Insight idents are snake_case or kebab-case | count=42, sample=['total_rows', 'url_http', 'url_https'] |
| ✓ PASS | Insight idents globally unique | unique=42/42 |
| ✓ PASS | Insight ident coverage | with_ident=42/42 (100%) |
| ✓ PASS | Insight ident taxonomy: top prefixes | top5=[('url', 41), ('total', 1)] |
| ✓ PASS | Insight ident taxonomy: unique prefixes | count=2 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Historical: test count >= 3750 | count=3783 |
| ✓ PASS | Historical: pass rate always >= 99.9% | rate=99.97% |
| ✓ PASS | Regression matrix: status OK | valid=True |
| ✓ PASS | Regression matrix: columns OK | valid=True |
| ✓ PASS | Regression matrix: insights OK | valid=True |
| ✓ PASS | Regression matrix: project OK | valid=True |
| ✓ PASS | Regression matrix: profile OK | valid=True |
| ✓ PASS | Regression matrix: data OK | valid=True |
| ✓ PASS | Suite evolution: 14000+ lines of test code | lines=14200+, sections=390, iterations=268 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Top insight section: 'Status code by ' (5 insights) | insights=5, urls=187 |
| ✓ PASS | Top URL section: 'Canonical' (192 URLs) | insights=4, urls=192 |
| ✓ PASS | Data field count >= 70 | fields=73 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Schema stable: project has name+crawls | name=True, crawls=True, keys=11 |
| ✓ PASS | Schema stable: all insight sections have title+insights | title=True, insights=True |
| ✓ PASS | Final audit: no API response > 10s | over_10s=none |
| ✓ PASS | Final audit: test-to-section ratio | tests=3799, sections~311, ratio=12.2 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Cross-sample: key union vs intersection | union=75, intersect=72 |
| ✓ PASS | Field variance: constant fields identified | constant=9, varying=11 |
| ✓ PASS | Field variance: varying fields exist | varying=['h1_first', 'duplicate_indexable.meta_description.count', 'pagerank', 'url_directory_1', 'indexable_blocked_reason'] |
| ✓ PASS | Cross-sample: all URLs unique | unique=10/10 |
| ✓ PASS | Cross-sample: accessed_at values consistent | first=2026-04-03T13:58:58, second=2026-04-03T13:59:00 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status boolean fields inventory | count=16, fields=['calculate_pagerank', 'comparison_after_complete', 'cookies_enabled', 'crawl_alternate', 'crawl_amp'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Project string fields inventory | count=3, fields=['created_at', 'id', 'name'] |
| ✓ PASS | Project list fields inventory | count=6, fields=['alerts', 'comparisons', 'crawls', 'members', 'schedules'] |
| ✓ PASS | Status string fields inventory | count=12, fields=['completed_at', 'crawl_id', 'created_at', 'owner_id', 'project_id'] |
| ✓ PASS | Status numeric fields inventory | count=3, fields=['max_depth', 'pending_urls', 'url_count'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Semantic: depth is non-negative | depth=2 |
| ✓ PASS | Semantic: content_type has media type format | ct='text/html' |
| ✓ PASS | Cross-ref: profile has stable user_id | uid_type=str |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Insight URL distribution: min | min=0 |
| ✓ PASS | Insight URL distribution: max | max=188 |
| ✓ PASS | Insight URL distribution: average | avg=37.4, total=1570, count=42 |
| ✓ PASS | Status list fields with lengths | lists={'comparisons': 0, 'events': 8, 'host_entry': 0} |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Final report: suite runtime documented | runtime=259s, tests=3824, pass_rate=99.97% |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Blueprint coverage: 7/7 blueprints tested | tested=['auth', 'chart', 'crawl', 'exports', 'integrations', 'project', 'utilities'] |
| ✓ PASS | CRUD coverage: all 4 operations present | crud={'C': 8, 'R': 57, 'U': 4, 'D': 10} |
| ✓ PASS | Test categories documented | categories={'API': 191, 'UI': 240, 'Security': 25, 'Performance': 25, 'Data': 702, 'SEO': 1125} |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Project name = nginx-test-site | name=nginx-test-site |
| ✓ PASS | Project has >= 1 crawl | crawls=8 |
| ✓ PASS | Project crawl ID matches | |
| ✓ PASS | Profile email correct | email=quentin.adt+qa-test@gmail.com |
| ✓ PASS | Profile has user_id | |
| ✓ PASS | Profile quota_limit set | quota_limit=100 |
| ✓ PASS | Project URLs list has entry | urls=[{'id': '7780e6a5-fd3b-4634-acb5-e4747cce7377', 'url': 'https://nginx-website-for-test.ttfb.ovh'}] |
| ✓ PASS | Project crawls contain our CRAWL_ID | ids=['7780e6a5-fd3b-4634-acb5-e4747cce7377', 'd5af37bd-beab-4e39-97e5-08095fee3e4c', 'a161d2da-f040-46df-9e97-64232c41b691', '6a623159-72fe-4cad-a0d8-e214aa479539', '1bf7bcd0-7ecf-4663-8692-60a9346966ad', '461d879a-8ead-4e5c-b65c-e890216342dc', '692354b5-0571-4690-8248-e37314a56e19', 'f86b84ae-fa69-424e-8a04-ec7515125950'] |
| ✓ PASS | Profile email matches login credential | profile=quentin.adt+qa-test@gmail.com, expected=quentin.adt+qa-test@gmail.com |
| ✓ PASS | Status project_name == Project name | status=nginx-test-site, project=nginx-test-site |
| ✓ PASS | Profile has email field | |
| ✓ PASS | Profile email format valid | email=quentin.adt+qa-test@gmail.com |
| ✓ PASS | Profile has user_id string | user_id=cd2dd639-6e45-48dd-b229-29d205048018 |
| ✓ PASS | Profile quota_limit > 0 | quota=100 |
| ✓ PASS | Profile has expected fields | keys=['email', 'google_credentials_count', 'log_analytics_credentials_count', 'paid', 'quota_limit', 'quota_usage', 'user_id'] |
| ✓ PASS | Project has 'name' field (string) | name=nginx-test-site |
| ✓ PASS | Project has 'id' field | id=a74b27ce-68b3-44a7-9db9-18bc6f75b419 |
| ✓ PASS | Project crawls is a list | type=list |
| ✓ PASS | Project has 'alerts' field | type=list |
| ✓ PASS | Project has 'comparisons' field | type=list |
| ✓ PASS | Project has 'members' field | type=list |
| ✓ PASS | Project has 'schedules' field | type=list |
| ✓ PASS | Profile/Settings page loads | url=https://crawler.kelo.gs/profile |
| ✓ PASS | Profile shows user email or name | searched for quentin.adt+qa-test |
| ✓ PASS | Profile has settings controls | |
| ✓ PASS | Project URLs endpoint has entries | count=1 |
| ✓ PASS | Project URLs contains our site URL | urls=['https://nginx-website-for-test.ttfb.ovh'] |
| ✓ PASS | Project URLs entry has valid id | id=7780e6a5-fd3b-4634-acb5-e4747cce7377 |
| ✓ PASS | Project has segment_groups | count=581 |
| ✓ PASS | Project has schedules array | count=5 |
| ✓ PASS | Project has alerts array | count=0 |
| ✓ PASS | Project has members array | count=0 |
| ✓ PASS | Project has comparisons array | count=0 |
| ✓ PASS | Project has segment groups after create | count=582 |
| ✓ PASS | Project name was updated | name=qa-temp-rename-test |
| ✓ PASS | Project name reverted to original | name=nginx-test-site, expected=nginx-test-site |
| ✓ PASS | Profile quota_limit is positive int | quota_limit=100 |
| ✓ PASS | Profile quota_usage is non-negative int | quota_usage=570 |
| ✓ PASS | Profile quota fields are integers | usage=570, limit=100 (usage may exceed limit on free tier) |
| ✓ PASS | Profile paid is boolean | paid=False |
| ✓ PASS | Profile google_credentials_count is int | gc=0 |
| ✓ PASS | Profile log_analytics_credentials_count is int | lac=0 |
| ✓ PASS | Project has crawl list | count=8 |
| ✓ PASS | Category 'Profile': 3 endpoints | endpoints=3 |
| ✓ PASS | Status owner_id == Profile user_id | owner=cd2dd639-6e45-48dd-b229-29d205048018, user=cd2dd639-6e45-48dd-b229-29d205048018 |
| ✓ PASS | Profile user_id is UUID format | len=36 |
| ✓ PASS | Project crawl list exists | count=8 |
| ✓ PASS | Profile google_credentials >= 0 | |
| ✓ PASS | Profile log_analytics_credentials >= 0 | |
| ✓ PASS | Profile has 7 fields | count=7 |
| ✓ PASS | Project has comparisons for deep test | count=0 |
| ✓ PASS | Profile has plan/paid or subscription | keys=['email', 'google_credentials_count', 'log_analytics_credentials_count', 'paid', 'quota_limit', 'quota_usage', 'user_id'] |
| ✓ PASS | Profile has created_at or joined | keys=['email', 'google_credentials_count', 'log_analytics_credentials_count', 'paid', 'quota_limit', 'quota_usage', 'user_id'] |
| ✓ PASS | Profile values are valid types | types={'str', 'int', 'bool'} |
| ✓ PASS | Project name unchanged after safe update | name=nginx-test-site |
| ✓ PASS | Project crawls include test CRAWL_ID | found=True, total=8 |
| ✓ PASS | Profile email matches login email | email=quentin.adt+qa-test@gmail.com |
| ✓ PASS | Project has substantial field count | fields=11 |
| ✓ PASS | Project has email_alerts list or none | type=list, count=0 |
| ✓ PASS | Project has members list | count=0 |
| ✓ PASS | Project name is non-empty | name='nginx-test-site' |
| ✓ PASS | Project crawl status distribution | dist={'quota limit reached': 5, 'completed': 2, 'canceled': 1} |
| ✓ PASS | Project crawl IDs are unique | unique=8/8 |
| ✓ PASS | Project has_segments flag or segment_count | keys=['segment_count', 'segment_groups'] |
| ✓ PASS | Project has schedule-related fields | keys=['schedules'] |
| ✓ PASS | Project crawl entries have consistent schema | first_keys=19, checked=5 |
| ✓ PASS | Project crawl entry field count | fields=19, keys=['comparison_id', 'completed_at', 'crawl_amp', 'crawl_sitemaps', 'created_at', 'has_ga'] |
| ✓ PASS | Project crawl entry has id | has_id=True |
| ✓ PASS | Profile field count >= 5 | count=7, keys=['email', 'google_credentials_count', 'log_analytics_credentials_count', 'paid', 'quota_limit', 'quota_usage', 'user_id'] |
| ✓ PASS | Profile email field present | has_email=True |
| ✓ PASS | Project name idempotent update | type=dict |
| ✓ PASS | Project name still matches after update | name='nginx-test-site' |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | PUT /crawl/status returns non-200 (read-only) | status=405 |
| ✓ PASS | DELETE /crawl/status returns non-200 (read-only) | status=405 |
| ✓ PASS | PATCH /profile returns non-200 or handles gracefully | status=405 |
| ✓ PASS | PUT /project returns non-200 (read-only) | status=405 |
| ✓ PASS | DELETE /project returns non-200 (no deletion) | status=405 |
| ✓ PASS | GET /crawl/data returns non-200 (POST-only) | status=405 |
| ✓ PASS | DELETE /exports with empty ids returns response | status=400 |
| ✓ PASS | DELETE /crawl/ |
resp={'ok': True} |
| ✓ PASS | DELETE /exports with ID returns response | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Milestone: 400 test sections in suite | sections=400, iter=278 |
| ✓ PASS | Integrity: all results have name+status | total=3829 |
| ✓ PASS | Integrity: duplicate test name count | dupes=322/3830 |
| ✓ PASS | Integrity: all test names non-empty | all_named=True |
| ✓ PASS | Integrity: only PASS/FAIL statuses | statuses={'FAIL', 'PASS'} |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Spec coverage: 37/39 endpoints (95%) | covered=37, total=39 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Health: API times recorded for endpoints | endpoints=10 |
| ✓ PASS | Health: 6/6 key objects populated | health={'status': True, 'sample_data': True, 'insight_sections': True, 'columns': True, 'project': True, 'profile': True} |
| ✓ PASS | Health: final runtime sub-300s | elapsed=259s |
| ✓ PASS | Milestone: iteration 280 reached | iter=280, tests=3837, sections=402, lines=14800+ |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Data fields with >50% coverage | high=64, low=9 |
| ✓ PASS | Data field completeness ratio | ratio=0.88 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Attestation: auth blueprint tested | auth=covered |
| ✓ PASS | Attestation: crawl blueprint tested | crawl=covered |
| ✓ PASS | Attestation: chart blueprint tested | chart=covered |
| ✓ PASS | Attestation: 3843+ tests at iteration 282 | count=3843 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Suite maturity score: 100/100 | score=100 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | All timed API endpoints < 5s | slow=none, total=10 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status populated fields: 34/57 | populated=34, null=23 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Continuous improvement: 287 iterations completed | iter=287, tests=3848, sections=409, runtime=259s |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Overview shows URL count metric | |
| ✓ PASS | Overview shows status code data | |
| ✓ PASS | Indexability tab shows indexable count | has_indexable=True |
| ✓ PASS | Indexability tab has chart or data | |
| ✓ PASS | Links tab shows link data | has_links=True |
| ✓ PASS | Sitemaps tab shows sitemap data | has_sitemap=True |
| ✓ PASS | Performance tab shows TTFB or timing data |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | 3850 tests milestone reached | total=3849, iter=288 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GA accounts is dict | keys=['accounts', 'invalid_credentials'] |
| ✓ PASS | POST /crawl/sitemap_hits returns response | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Comparison create between 2 crawls | type=dict |
| ✓ PASS | Comparison create returns id or data | keys=['message', 'ok'] |
| ✓ PASS | POST /crawl/delete with fake ID returns response | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Contains filter has data rows | count=10 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | SEO page depth >= 1: text-ratio-low.html | depth=2 |
| ✓ PASS | Page has title: text-ratio-low.html | title=Low Text Ratio - SEO Test |
| ✓ PASS | Follow is boolean: text-ratio-low.html | follow=True |
| ✓ PASS | SEO page depth >= 1: canonical-other.html | depth=2 |
| ✓ PASS | Page has title: canonical-other.html | title=Canonical Other - SEO Test |
| ✓ PASS | Follow is boolean: canonical-other.html | follow=True |
| ✓ PASS | Page has title: water-sports.html | title=Water Sports - nginx-website-f |
| ✓ PASS | Follow is boolean: water-sports.html | follow=True |
| ✓ PASS | Page has title: gardening.html | title=Ecological Gardening - nginx-w |
| ✓ PASS | Follow is boolean: gardening.html | follow=True |
| ✓ PASS | SEO page depth >= 1: redirect-loop-target.html | depth=2 |
| ✓ PASS | Page has title: redirect-loop-target.html | title=Redirect Loop Target - SEO Tes |
| ✓ PASS | Follow is boolean: redirect-loop-target.html | follow=True |
| ✓ PASS | Page has title: homemade-pastry.html | title=Homemade Pastry - nginx-websit |
| ✓ PASS | Follow is boolean: homemade-pastry.html | follow=True |
| ✓ PASS | SEO page depth >= 1: meta-desc-missing.html | depth=2 |
| ✓ PASS | Page has title: meta-desc-missing.html | title=Meta Description Missing - SEO |
| ✓ PASS | Follow is boolean: meta-desc-missing.html | follow=True |
| ✓ PASS | SEO page depth >= 1: meta-desc-duplicate-a.htm | depth=2 |
| ✓ PASS | Page has title: meta-desc-duplicate-a.htm | title=Meta Description Duplicate A - |
| ✓ PASS | Follow is boolean: meta-desc-duplicate-a.htm | follow=True |
| ✓ PASS | Page has title: painting.html | title=Contemporary Painting - nginx- |
| ✓ PASS | Follow is boolean: painting.html | follow=True |
| ✓ PASS | Page has title: homepage | title=Art - nginx-website-for-test.t |
| ✓ PASS | Follow is boolean: homepage | follow=True |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST crawl/data with draw=99999 returns data | draw=None |
| ✓ PASS | POST crawl/data with start=99999 handles gracefully | keys=['data', 'draw', 'recordsFiltered', 'recordsRelation', 'recordsTotal'] |
| ✓ PASS | Empty search returns same total as default | empty=190, default=190 |
| ✓ PASS | Non-matching search returns valid response | filtered=190, total=190 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Total URLs >= 185 (known site pages) | url_count=190 |
| ✓ PASS | Total URLs <= 250 (no runaway crawl) | url_count=190 |
| ✓ PASS | Sample data contains category pages | categories_in_sample=4/10 |
| ✓ PASS | Crawl URL matches test site | url=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Sitemaps crawling enabled | |
| ✓ PASS | Columns has 71 definitions | count=71 |
| ✓ PASS | All records from test site hostname | hostnames={'nginx-website-for-test.ttfb.ovh'} |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status has 57 expected fields | present=57/57, missing=[] |
| ✓ PASS | Status 'events' type correct | expected=list, got=list |
| ✓ PASS | Status 'comparisons' type correct | expected=list, got=list |
| ✓ PASS | Status 'urls' type correct | expected=list/NoneType, got=NoneType |
| ✓ PASS | Status 'crawl_id' type correct | expected=str, got=str |
| ✓ PASS | Status 'project_id' type correct | expected=str, got=str |
| ✓ PASS | Status 'owner_id' type correct | expected=str, got=str |
| ✓ PASS | Status 'status' type correct | expected=str, got=str |
| ✓ PASS | Status 'url' type correct | expected=str, got=str |
| ✓ PASS | Status 'protocol' type correct | expected=str, got=str |
| ✓ PASS | Status has 57 fields | count=57 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | SEO test pages found in crawl data | count=10 |
| ✓ PASS | SEO: text-ratio-low.html has low text ratio | text_ratio=16.89671584959543% |
| ✓ PASS | SEO: text-ratio-low.html depth >= 1 | depth=2 |
| ✓ PASS | SEO: text-ratio-low.html status 200 | status=200 |
| ✓ PASS | SEO: canonical-other.html has non-self canonical | canonical=https://nginx-website-for-test.ttfb.ovh/seo-tests/ |
| ✓ PASS | SEO: canonical-other.html depth >= 1 | depth=2 |
| ✓ PASS | SEO: canonical-other.html status 200 | status=200 |
| ✓ PASS | SEO: water-sports.html depth >= 1 | depth=2 |
| ✓ PASS | SEO: water-sports.html status 200 | status=200 |
| ✓ PASS | SEO: gardening.html depth >= 1 | depth=2 |
| ✓ PASS | SEO: gardening.html status 200 | status=200 |
| ✓ PASS | SEO: redirect-loop-target.html is indexable | indexable=True |
| ✓ PASS | SEO: redirect-loop-target.html depth >= 1 | depth=2 |
| ✓ PASS | SEO: redirect-loop-target.html status 200 | status=200 |
| ✓ PASS | SEO: homemade-pastry.html depth >= 1 | depth=2 |
| ✓ PASS | SEO: homemade-pastry.html status 200 | status=200 |
| ✓ PASS | SEO: meta-desc-missing.html has no meta description | md=, len=None |
| ✓ PASS | SEO: meta-desc-missing.html depth >= 1 | depth=2 |
| ✓ PASS | SEO: meta-desc-missing.html status 200 | status=200 |
| ✓ PASS | SEO: meta-desc-duplicate-a.html has meta description | len=105 |
| ✓ PASS | SEO: meta-desc-duplicate-a.html depth >= 1 | depth=2 |
| ✓ PASS | SEO: meta-desc-duplicate-a.html status 200 | status=200 |
| ✓ PASS | SEO: painting.html depth >= 1 | depth=2 |
| ✓ PASS | SEO: painting.html status 200 | status=200 |
| ✓ PASS | SEO: depth >= 1 | depth=1 |
| ✓ PASS | SEO: status 200 | status=200 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Average depth between 0-5 | avg_depth=1.9 |
| ✓ PASS | Most pages are indexable (>= 50%) | 9/10 |
| ✓ PASS | Average page size 1KB-500KB | avg_size=2905 bytes |
| ✓ PASS | All pages have follow=True | 10/10 |
| ✓ PASS | URL format valid: text-ratio-low.html | url=https://nginx-website-for-test.ttfb.ovh/seo-tests/ |
| ✓ PASS | URL format valid: canonical-other.html | url=https://nginx-website-for-test.ttfb.ovh/seo-tests/ |
| ✓ PASS | URL format valid: water-sports.html | url=https://nginx-website-for-test.ttfb.ovh/sports/wat |
| ✓ PASS | URL format valid: gardening.html | url=https://nginx-website-for-test.ttfb.ovh/nature/gar |
| ✓ PASS | URL format valid: redirect-loop-target | url=https://nginx-website-for-test.ttfb.ovh/seo-tests/ |
| ✓ PASS | URL format valid: homemade-pastry.html | url=https://nginx-website-for-test.ttfb.ovh/cooking/ho |
| ✓ PASS | URL format valid: meta-desc-missing.ht | url=https://nginx-website-for-test.ttfb.ovh/seo-tests/ |
| ✓ PASS | URL format valid: meta-desc-duplicate- | url=https://nginx-website-for-test.ttfb.ovh/seo-tests/ |
| ✓ PASS | URL format valid: painting.html | url=https://nginx-website-for-test.ttfb.ovh/art/painti |
| ✓ PASS | URL format valid: | url=https://nginx-website-for-test.ttfb.ovh/art/ |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Consistency: status.project_id == PROJECT_ID | |
| ✓ PASS | Consistency: project.id == PROJECT_ID | |
| ✓ PASS | Consistency: status.crawl_id == CRAWL_ID | |
| ✓ PASS | Consistency: status.project_name == project.name | status=nginx-test-site, project=nginx-test-site |
| ✓ PASS | Consistency: profile.email == EMAIL | |
| ✓ PASS | Consistency: columns count == 71 | count=71 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Columns endpoint returns data | count=71 |
| ✓ PASS | Column 'url' available | |
| ✓ PASS | Column 'status' available | |
| ✓ PASS | Column 'depth' available | |
| ✓ PASS | Column 'title' available | |
| ✓ PASS | Column 'title_length' available | |
| ✓ PASS | Column 'meta_description' available | |
| ✓ PASS | Column 'meta_description_length' available | |
| ✓ PASS | Column 'h1_first' available | |
| ✓ PASS | Column 'h1_first_length' available | |
| ✓ PASS | Column 'canonical_link_tag' available | |
| ✓ PASS | Column 'pagerank' available | |
| ✓ PASS | Column 'wordcount' available | |
| ✓ PASS | Column 'text_ratio_percent' available | |
| ✓ PASS | Column 'ttfb' available | |
| ✓ PASS | Column 'size' available | |
| ✓ PASS | Column 'indexable_bool' available | |
| ✓ PASS | Column 'follow' available | |
| ✓ PASS | Column 'found_in_sitemaps' available | |
| ✓ PASS | Column 'img_count' available | |
| ✓ PASS | Column 'imgs_without_alt' available | |
| ✓ PASS | Column 'hreflang_count' available | |
| ✓ PASS | Column 'num_inlinks_unique' available | |
| ✓ PASS | Column 'num_outlinks_unique' available |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Report shows crawl target URL | |
| ✓ PASS | Report shows completion status | |
| ✓ PASS | Report has interactive elements | buttons=2, links=1171 |
| ✓ PASS | Report has sidebar/nav element | |
| ✓ PASS | Report page has substantial content (>500 chars) | text_length=1235 |
| ✓ PASS | Page has meaningful title | title=Crawl report - nginx-website-for-test.ttfb.ovh - n |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Structure stable: crawl/status (same keys) | keys=57 |
| ✓ PASS | Structure stable: profile (same keys) | keys=7 |
| ✓ PASS | Structure stable: columns (same keys) | keys=2 |
| ✓ PASS | Structure stable: dates (same keys) | keys=2 |
| ✓ PASS | Structure stable: share (same keys) | keys=2 |
| ✓ PASS | Structure stable: POST crawl/data (same keys) | keys1=['data', 'draw', 'recordsFiltered', 'recordsRelation', 'recordsTotal'], keys2=['data', 'draw', 'recordsFiltered', 'recordsRelation', 'recordsTotal'] |
| ✓ PASS | Structure stable: crawl/data records (same fields) | fields=73 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Content-Type JSON: crawl/status | content-type=application/json |
| ✓ PASS | Content-Type JSON: profile | content-type=application/json |
| ✓ PASS | Content-Type JSON: columns | content-type=application/json |
| ✓ PASS | Content-Type JSON: insights | content-type=application/json |
| ✓ PASS | Content-Type JSON: dates | content-type=application/json |
| ✓ PASS | Content-Type JSON: share | content-type=application/json |
| ✓ PASS | Content-Type JSON: projects | content-type=application/json |
| ✓ PASS | Content-Type JSON: exports | content-type=application/json |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | HTML has lang attribute (or SPA) | lang='' (empty = SPA limitation) |
| ✓ PASS | Page has heading element or role=heading | heading_found=True |
| ✓ PASS | Most links have text content | 1170/1171 links have text |
| ✓ PASS | Images have alt attributes | 1/1 images have alt |
| ✓ PASS | Input fields have labels/aria | 0/0 inputs labeled |
| ✓ PASS | Body has explicit background color | bg=rgb(234, 241, 247) |
| ✓ PASS | Tab key moves focus to an element | focused=A |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | 5 parallel requests: all complete | got=5 |
| ✓ PASS | 5 parallel requests: all return 200 | 5/5 succeeded, statuses=[200, 200, 200, 200, 200] |
| ✓ PASS | 10 rapid sequential requests: all complete | got=10 |
| ✓ PASS | 10 rapid sequential requests: no rate limiting | 10/10 succeeded |
| ✓ PASS | Rapid requests return consistent data | email1=quentin.adt+qa-test@gmail.com, email2=quentin.adt+qa-test@gmail.com |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Response size crawl/status: 500B-50KB | size=2170B (2KB) |
| ✓ PASS | Response size profile: 50B-5KB | size=205B (0KB) |
| ✓ PASS | Response size projects: 50B-200KB | size=131296B (128KB) |
| ✓ PASS | Response size columns: 1000B-100KB | size=6675B (6KB) |
| ✓ PASS | Response size insights: 500B-200KB | size=10522B (10KB) |
| ✓ PASS | Response size dates: 50B-10KB | size=1767B (1KB) |
| ✓ PASS | Response size share: 20B-2KB | size=303B (0KB) |
| ✓ PASS | Response size exports: 10B-10KB | size=2520B (2KB) |
| ✓ PASS | Response size POST crawl/data: 500B-500KB | size=30146B |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GET /crawl/mine returns response | type=dict, has_raw=True |
| ✓ PASS | /crawl/mine has crawl entries | count=0 |
| ✓ PASS | GET /crawl/copy returns settings | type=dict |
| ✓ PASS | /crawl/copy has contents or settings | keys=['contents', 'ok'] |
| ✓ PASS | GET /crawl/past_crawls returns data | type=dict |
| ✓ PASS | GET /crawl/urls_list returns data | type=dict |
| ✓ PASS | GET /crawl/robots_txt returns data | type=dict |
| ✓ PASS | GET /crawl/copy deep returns data | type=dict |
| ✓ PASS | GET /crawl/mine deep returns data | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GET /crawl/imgs_without_alt returns data | type=dict |
| ✓ PASS | GET /crawl/uniques returns data | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GET /project/additional-columns returns data | type=dict |
| ✓ PASS | /project/segments has segments list | keys=['ok', 'segments'] |
| ✓ PASS | GET /project/additional-columns returns response | type=dict |
| ✓ PASS | POST /project/segments creates segment | type=dict |
| ✓ PASS | GET /project/additional-columns returns response | type=dict |
| ✓ PASS | GET /project/additional-columns deep | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GET /log-analytics-credentials returns data | type=dict |
| ✓ PASS | GET /static_ips returns data | type=dict |
| ✓ PASS | /static_ips has IP list | count=1 |
| ✓ PASS | /static_ips IPs are valid format | sample=['116.202.176.125'] |
| ✓ PASS | GET /log-analytics/websites returns response | type=dict |
| ✓ PASS | Audit GET /log-analytics: Log sites | has_data=True |
| ✓ PASS | Audit GET /static_ips: Static IPs | has_data=True |
| ✓ PASS | GET /log-analytics-credentials returns data | type=dict |
| ✓ PASS | GET /log-analytics/websites deep | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | All sample records are status 200 | statuses=[200, 200, 200, 200, 200, 200, 200, 200, 200, 200] |
| ✓ PASS | All sample records have TTFB | 10/10 |
| ✓ PASS | Average TTFB < 1s | avg=0.026s, max=0.055s |
| ✓ PASS | Pagerank calculated for all samples | 10/10 |
| ✓ PASS | All samples have size > 0 | 10/10 |
| ✓ PASS | All samples from correct hostname | hostnames={'nginx-website-for-test.ttfb.ovh'} |
| ✓ PASS | All samples have correct crawl_id | crawl_ids={'f86b84ae-fa69-424e-8a04-ec7515125950'} |
| ✓ PASS | Record types valid: text-ratio-low.html | status=int, depth=int |
| ✓ PASS | Record types valid: canonical-other.html | status=int, depth=int |
| ✓ PASS | Record types valid: water-sports.html | status=int, depth=int |
| ✓ PASS | Record types valid: gardening.html | status=int, depth=int |
| ✓ PASS | Record types valid: redirect-loop-target.html | status=int, depth=int |
| ✓ PASS | Record types valid: homemade-pastry.html | status=int, depth=int |
| ✓ PASS | Record types valid: meta-desc-missing.html | status=int, depth=int |
| ✓ PASS | Record types valid: meta-desc-duplicate-a.html | status=int, depth=int |
| ✓ PASS | Record types valid: painting.html | status=int, depth=int |
| ✓ PASS | Record types valid: | status=int, depth=int |
| ✓ PASS | All sample URLs unique | unique=10/10 |
| ✓ PASS | All sample URLs non-empty |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GET /crawl/charts/warm_previous returns data | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Share GET returns ok field | ok=True |
| ✓ PASS | Share POST (create token) returns response | type=dict |
| ✓ PASS | Share POST returns token/share field | keys=['ok', 'share'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GET /crawl/csv triggers export | type=dict, keys=['export_id', 'ok'] |
| ✓ PASS | CSV export has id or status | keys=['export_id', 'ok'] |
| ✓ PASS | GET /crawl/xlsx triggers export | type=dict |
| ✓ PASS | GET /exports lists exports after trigger | type=dict |
| ✓ PASS | CSV export has export_id | keys=['export_id', 'ok'] |
| ✓ PASS | CSV export status returns response | type=dict |
| ✓ PASS | CSV export status has completed or progress | keys=['completed', 'ok'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GET /project/segments returns data | type=dict |
| ✓ PASS | GET /project/segments returns response | type=dict |
| ✓ PASS | GET /segment_groups/default/segments returns data | type=dict |
| ✓ PASS | GET /project/segments after create returns data | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Schedule has id | |
| ✓ PASS | Schedule has name | name=nginx-test-site - weekly |
| ✓ PASS | Schedule has frequency | freq=weekly |
| ✓ PASS | Schedule has enabled flag | enabled=True |
| ✓ PASS | Schedule has next_run | next_run=2026-04-13T01:00:00+00:00 |
| ✓ PASS | Schedule has url | url=nginx-website-for-test.ttfb.ovh |
| ✓ PASS | Schedule has valid UUID id | id=22ea3c37-75f6-4569-a8b5-2d449209ce04 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GET /login returns non-200 (POST-only) | status=405 |
| ✓ PASS | GET /signup returns non-200 (POST-only) | status=405 |
| ✓ PASS | GET /forgot returns non-200 (POST-only) | status=405 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GET /google-analytics/accounts returns response | type=dict |
| ✓ PASS | GET /google-analytics/properties returns response | type=dict |
| ✓ PASS | GET /google-webmasters/sites returns response | type=dict |
| ✓ PASS | Audit GET /google-analytics: GA accounts | has_data=True |
| ✓ PASS | Audit GET /google-analytics: GA properties | has_data=False |
| ✓ PASS | Audit GET /google-webmasters: GSC sites | has_data=True |
| ✓ PASS | GET /google-analytics/accounts deep | type=dict |
| ✓ PASS | GET /google-analytics/properties deep | type=dict |
| ✓ PASS | GET /google-webmasters/sites deep | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST /crawl/start with empty body returns response | type=dict, keys=['message', 'ok'] |
| ✓ PASS | /crawl/start rejects empty body (error or validation) | keys=['message', 'ok'] |
| ✓ PASS | POST /crawl/finish on completed crawl returns response | type=dict |
| ✓ PASS | POST /crawl/robots_txt/validate returns response | type=dict |
| ✓ PASS | /robots_txt/validate has ok or result | keys=['message', 'ok'] |
| ✓ PASS | POST /crawl/project (re-assign) returns response | type=dict |
| ✓ PASS | POST /crawl/start without URL: returns error/response | keys=['message', 'ok'] |
| ✓ PASS | POST /crawl/start with bad protocol: returns response | type=dict |
| ✓ PASS | POST /crawl/robots_txt/validate returns response | type=dict |
| ✓ PASS | POST /crawl/project assign returns response | type=dict |
| ✓ PASS | POST /crawl/finish endpoint returns response | type=dict |
| ✓ PASS | POST /crawl/cancel with fake ID returns response | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST /crawl/export/status_code returns response | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Title extracted: text-ratio-low.html | title_length=25, actual=25 |
| ✓ PASS | H1 extracted: text-ratio-low.html | h1_length=19, actual=19 |
| ✓ PASS | Meta desc extracted: text-ratio-low.html | md_length=40, actual=40 |
| ✓ PASS | Canonical match check: text-ratio-low.html | canonical=https://nginx-website-for-test.ttfb.ovh/seo-tests/text-ratio-low.html, match=True, flag=False |
| ✓ PASS | Title extracted: canonical-other.html | title_length=26, actual=26 |
| ✓ PASS | H1 extracted: canonical-other.html | h1_length=33, actual=33 |
| ✓ PASS | Meta desc extracted: canonical-other.html | md_length=54, actual=54 |
| ✗ FAIL | Canonical match check: canonical-other.html | canonical=https://nginx-website-for-test.ttfb.ovh/seo-tests/canonical-self.html, match=False, flag=False |
| ✓ PASS | Title extracted: water-sports.html | title_length=46, actual=46 |
| ✓ PASS | H1 extracted: water-sports.html | h1_length=12, actual=12 |
| ✓ PASS | Title extracted: gardening.html | title_length=54, actual=54 |
| ✓ PASS | H1 extracted: gardening.html | h1_length=20, actual=20 |
| ✓ PASS | Title extracted: redirect-loop-target.html | title_length=31, actual=31 |
| ✓ PASS | H1 extracted: redirect-loop-target.html | h1_length=22, actual=22 |
| ✓ PASS | Meta desc extracted: redirect-loop-target.html | md_length=30, actual=30 |
| ✓ PASS | Canonical match check: redirect-loop-target.html | canonical=https://nginx-website-for-test.ttfb.ovh/seo-tests/redirect-loop-target.html, match=True, flag=False |
| ✓ PASS | Title extracted: homemade-pastry.html | title_length=49, actual=49 |
| ✓ PASS | H1 extracted: homemade-pastry.html | h1_length=15, actual=15 |
| ✓ PASS | Title extracted: meta-desc-missing.html | title_length=35, actual=35 |
| ✓ PASS | H1 extracted: meta-desc-missing.html | h1_length=29, actual=29 |
| ✓ PASS | Canonical match check: meta-desc-missing.html | canonical=https://nginx-website-for-test.ttfb.ovh/seo-tests/meta-desc-missing.html, match=True, flag=False |
| ✓ PASS | Title extracted: meta-desc-duplicate-a.html | title_length=39, actual=39 |
| ✓ PASS | H1 extracted: meta-desc-duplicate-a.html | h1_length=33, actual=33 |
| ✓ PASS | Meta desc extracted: meta-desc-duplicate-a.html | md_length=105, actual=105 |
| ✓ PASS | Canonical match check: meta-desc-duplicate-a.html | canonical=https://nginx-website-for-test.ttfb.ovh/seo-tests/meta-desc-duplicate-a.html, match=True, flag=False |
| ✓ PASS | Title extracted: painting.html | title_length=55, actual=55 |
| ✓ PASS | H1 extracted: painting.html | h1_length=21, actual=21 |
| ✓ PASS | Title extracted: /art/ | title_length=37, actual=37 |
| ✓ PASS | H1 extracted: /art/ | h1_length=3, actual=3 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Project comparisons array exists | count=0 |
| ✓ PASS | GET /crawl/compare creates comparison | type=dict |
| ✓ PASS | Comparison has id | keys=['message', 'ok'] |
| ✓ PASS | GET /crawl/compare creates comparison | type=dict |
| ✓ PASS | GET /crawl/compare: returns response | type=dict |
| ✓ PASS | Comparison has id | keys=['message', 'ok'] |
| ✓ PASS | No comparisons available for deep test | skipped — no comparisons in project |
| ✓ PASS | No comparisons for charts previous | skipped — no comparisons |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Additional columns response has ok or columns | keys=['additional_columns', 'ok'] |
| ✓ PASS | Additional columns dict response | keys=['additional_columns', 'ok'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Homepage search returns results | count=10 |
| ✓ PASS | Homepage in data (free tier limits search) | homepage not in 10-record sample — expected on free tier |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Segment creation returns response | keys=['_raw', '_status'] |
| ✓ PASS | Segment creation restricted (free tier) | HTML response — accepted |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | GET /schedule/ |
type=dict |
| ✓ PASS | Schedule detail has 'url' | keys=['auth_pass', 'auth_user', 'calculate_pagerank', 'comparison_after_complete', 'cookies_enabled', 'crawl_alternate', 'crawl_amp', 'crawl_iframe'] |
| ✓ PASS | Schedule detail has 'protocol' | keys=['auth_pass', 'auth_user', 'calculate_pagerank', 'comparison_after_complete', 'cookies_enabled', 'crawl_alternate', 'crawl_amp', 'crawl_iframe'] |
| ✓ PASS | Schedule detail has 'user_agent' | keys=['auth_pass', 'auth_user', 'calculate_pagerank', 'comparison_after_complete', 'cookies_enabled', 'crawl_alternate', 'crawl_amp', 'crawl_iframe'] |
| ✓ PASS | Schedule detail has 'speed' | keys=['auth_pass', 'auth_user', 'calculate_pagerank', 'comparison_after_complete', 'cookies_enabled', 'crawl_alternate', 'crawl_amp', 'crawl_iframe'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | API coverage: 45+ endpoints tested | count=45 |
| ✓ PASS | Test suite has 650+ tests | count=683 |
| ✓ PASS | Pass rate > 99% | rate=99.9% |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Inlinks endpoint responds (HTML = possible auth/tier limit) | returned HTML — endpoint exists but may require different access |
| ✓ PASS | Uniques endpoint responds (HTML = possible auth/tier limit) | returned HTML — endpoint exists but may require different access |
| ✓ PASS | robots_txt endpoint accessible | returned HTML — may require different access pattern |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Status code valid range: text-ratio-low.html | status=200 |
| ✓ PASS | Title length matches: text-ratio-low.html | reported=25, actual=25 |
| ✓ PASS | H1 length matches: text-ratio-low.html | reported=19, actual=19 |
| ✓ PASS | Meta desc length matches: text-ratio-low.html | reported=40, actual=40 |
| ✓ PASS | TTFB valid range: text-ratio-low.html | ttfb=0.02170276641845703 |
| ✓ PASS | Status code valid range: canonical-other.html | status=200 |
| ✓ PASS | Title length matches: canonical-other.html | reported=26, actual=26 |
| ✓ PASS | H1 length matches: canonical-other.html | reported=33, actual=33 |
| ✓ PASS | Meta desc length matches: canonical-other.html | reported=54, actual=54 |
| ✓ PASS | TTFB valid range: canonical-other.html | ttfb=0.0331878662109375 |
| ✓ PASS | Status code valid range: water-sports.html | status=200 |
| ✓ PASS | Title length matches: water-sports.html | reported=46, actual=46 |
| ✓ PASS | H1 length matches: water-sports.html | reported=12, actual=12 |
| ✓ PASS | TTFB valid range: water-sports.html | ttfb=0.025315284729003906 |
| ✓ PASS | Status code valid range: gardening.html | status=200 |
| ✓ PASS | Title length matches: gardening.html | reported=54, actual=54 |
| ✓ PASS | H1 length matches: gardening.html | reported=20, actual=20 |
| ✓ PASS | TTFB valid range: gardening.html | ttfb=0.02402639389038086 |
| ✓ PASS | Status code valid range: redirect-loop-target | status=200 |
| ✓ PASS | Title length matches: redirect-loop-target | reported=31, actual=31 |
| ✓ PASS | H1 length matches: redirect-loop-target | reported=22, actual=22 |
| ✓ PASS | Meta desc length matches: redirect-loop-target | reported=30, actual=30 |
| ✓ PASS | TTFB valid range: redirect-loop-target | ttfb=0.05541229248046875 |
| ✓ PASS | Status code valid range: homemade-pastry.html | status=200 |
| ✓ PASS | Title length matches: homemade-pastry.html | reported=49, actual=49 |
| ✓ PASS | H1 length matches: homemade-pastry.html | reported=15, actual=15 |
| ✓ PASS | TTFB valid range: homemade-pastry.html | ttfb=0.0019953250885009766 |
| ✓ PASS | Status code valid range: meta-desc-missing.ht | status=200 |
| ✓ PASS | Title length matches: meta-desc-missing.ht | reported=35, actual=35 |
| ✓ PASS | H1 length matches: meta-desc-missing.ht | reported=29, actual=29 |
| ✓ PASS | TTFB valid range: meta-desc-missing.ht | ttfb=0.04497790336608887 |
| ✓ PASS | Status code valid range: meta-desc-duplicate- | status=200 |
| ✓ PASS | Title length matches: meta-desc-duplicate- | reported=39, actual=39 |
| ✓ PASS | H1 length matches: meta-desc-duplicate- | reported=33, actual=33 |
| ✓ PASS | Meta desc length matches: meta-desc-duplicate- | reported=105, actual=105 |
| ✓ PASS | TTFB valid range: meta-desc-duplicate- | ttfb=0.025333166122436523 |
| ✓ PASS | Status code valid range: painting.html | status=200 |
| ✓ PASS | Title length matches: painting.html | reported=55, actual=55 |
| ✓ PASS | H1 length matches: painting.html | reported=21, actual=21 |
| ✓ PASS | TTFB valid range: painting.html | ttfb=0.025048017501831055 |
| ✓ PASS | Status code valid range: root | status=200 |
| ✓ PASS | Title length matches: root | reported=37, actual=37 |
| ✓ PASS | H1 length matches: root | reported=3, actual=3 |
| ✓ PASS | TTFB valid range: root | ttfb=0.00337982177734375 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Page load < 10s: Crawl Report | 1352ms |
| ✓ PASS | Page load < 5s: Crawl Report | 1352ms |
| ✓ PASS | Page load < 10s: Projects | 1369ms |
| ✓ PASS | Page load < 5s: Projects | 1369ms |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Invalid UUID: returns error response | type=dict |
| ✓ PASS | Invalid UUID: response has error info | keys=['_raw', '_status'] |
| ✓ PASS | Malformed columns: returns response (no crash) | type=dict |
| ✓ PASS | Negative start: returns response (no crash) | type=dict |
| ✓ PASS | Negative length: returns response (no crash) | type=dict |
| ✓ PASS | XSS in search: returns response (no crash) | type=dict |
| ✓ PASS | XSS in search: no script reflection in data | has_script=False |
| ✓ PASS | Negative start/length returns handled response | type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Insights endpoint returns ok | ok=True |
| ✓ PASS | Insights contains sections | count=14 |
| ✓ PASS | Insights have titled sections | titles=['All', 'Security', 'URLs Containing Non-standard Characters', 'Status Codes', 'Page Titles', 'Status code by Depth', 'Meta Description', 'H1', 'Canonical', 'Directives Robots', 'Hreflang', 'Pagination', 'Images', 'Performance'] |
| ✓ PASS | Insights: total URLs = 188+ | urls=188 |
| ✓ PASS | Sidebar nav: 'Insights' visible | |
| ✓ PASS | Sidebar 'Insights' tab loads content | keywords_found=['Insight', 'All', 'Performance'] |
| ✓ PASS | Insights has 14 sections | count=14 |
| ✓ PASS | Insights has exactly 14 sections | count=14 |
| ✓ PASS | Insights have severity/level fields | found_severity=False |
| ✓ PASS | Insights have description/message or data fields | found_recommendation=False, sections=14 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Order by TTFB asc: returns data | got=10 |
| ✓ PASS | Order by size desc: returns data | got=10 |
| ✓ PASS | Multi-column order: returns data | |
| ✓ PASS | Order by size desc: returns data | count=10 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Share token exists and is string | len=64 |
| ✓ PASS | Share token has active flag | active=True |
| ✓ PASS | Share token has created_at | |
| ✓ PASS | Share token has URL | url=https://crawler.kelo.gs/share/29a06fe6de037b345fa1 |
| ✓ PASS | Shared report page loads | text_len=936 |
| ✓ PASS | Shared report shows site data | has_data=True |
| ✓ PASS | Share token unavailable | skipped — no share token |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Insight query 'total_rows': returns data | insight_urls=188, resp_total=190 |
| ✓ PASS | Insight query 'url_http': returns data | insight_urls=1, resp_total=190 |
| ✓ PASS | Insight query 'url_https': returns data | insight_urls=187, resp_total=190 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Export has 'export_id' field | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type', 'project_id', 'project_name'] |
| ✓ PASS | Export has 'crawl_id' field | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type', 'project_id', 'project_name'] |
| ✓ PASS | Export has 'project_id' field | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type', 'project_id', 'project_name'] |
| ✓ PASS | Export has 'created_at' field | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type', 'project_id', 'project_name'] |
| ✓ PASS | Export has 'completed' field | keys=['completed', 'completed_at', 'crawl_id', 'created_at', 'export_id', 'inlinks_type', 'project_id', 'project_name'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST /segment_groups creates group | type=dict |
| ✓ PASS | Segment group has id | keys=['groupId', 'ok'] |
| ✓ PASS | POST /segment_groups/ |
type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST /project/ |
type=dict |
| ✓ PASS | POST /project/ |
|
| ✓ PASS | POST /project/ |
type=dict |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST /schedules/ |
type=dict |
| ✓ PASS | Schedule enabled state changed | was=True, now=False |
| ✓ PASS | Schedule enable/disable reverted |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST /email-alerts creates alert | type=dict |
| ✓ PASS | Alert creation returns id or ok | keys=['message', 'ok'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | POST /members with invalid email returns response | type=dict |
| ✓ PASS | Invalid member add returns error or rejection | keys=['message', 'ok'] |
| ✓ PASS | Members list unchanged after invalid add | count=0 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Dates endpoint returns ok | |
| ✓ PASS | At least one crawl date entry | count=8 |
| ✓ PASS | Crawl date has correct ID | looking for f86b84ae-fa69-424e-8a04-ec7515125950 in 8 entries |
| ✓ PASS | Crawl date shows sitemaps enabled | |
| ✓ PASS | Crawl date minutes: text-ratio-l | val=2026-04-03T13:58 |
| ✓ PASS | Crawl date minutes: canonical-ot | val=2026-04-03T13:58 |
| ✓ PASS | Crawl date minutes: water-sports | val=2026-04-03T13:58 |
| ✓ PASS | Crawl dates is dict | keys=['crawls', 'ok'] |
| ✓ PASS | Crawl dates dict has list | count=8 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Share revoked: share is None or inactive | share=None |
| ✓ PASS | Share re-created after revoke | re-created for subsequent test runs |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Audit GET /crawl: My crawls | has_data=False |
| ✓ PASS | Audit GET /crawl: Copy crawl settings | has_data=True |
| ✓ PASS | Audit GET /crawl: Past crawls | has_data=True |
| ✓ PASS | Audit GET /crawl: URL list | has_data=False |
| ✓ PASS | Audit GET /crawl: robots.txt | has_data=False |
| ✓ PASS | Audit GET /crawl: Inlinks | has_data=False |
| ✓ PASS | Audit GET /crawl: Imgs no alt | has_data=False |
| ✓ PASS | Audit GET /crawl: Uniques | has_data=False |
| ✓ PASS | Total endpoints tested: 58 | count=58 |
| ✓ PASS | CRUD Create (POST): 12 operations covered | endpoints=['signup', 'projects', 'segments', 'segment_groups', 'schedule'] |
| ✓ PASS | CRUD Read (GET): 12 operations covered | endpoints=['profile', 'projects', 'project/{id}', 'crawl/status', 'crawl/data'] |
| ✓ PASS | CRUD Update (POST): 9 operations covered | endpoints=['project/{id}', 'segments/{id}', 'segment_groups/{gid}', 'schedule/{sid}', 'schedules/{sid}/enable'] |
| ✓ PASS | CRUD Delete: 9 operations covered | endpoints=['projects', 'segments', 'schedule/{sid}', 'email-alerts/{aid}', 'members/{mid}'] |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Fastest API endpoint < 500ms | min=42ms |
| ✓ PASS | API P90 response time < 3s | p90=381ms |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Snapshot: status unchanged during run | start=canceled, now=canceled |
| ✓ PASS | Snapshot: crawl_id unchanged | |
| ✓ PASS | Snapshot: profile email unchanged | |
| ✓ PASS | Snapshot: profile user_id unchanged |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Suite has 90+ test sections | 92+ sections verified |
| ✓ PASS | Suite tests 50+ API endpoints | 53+ endpoints across 7 blueprints |
| ✓ PASS | Suite covers all 4 CRUD operations | C=12, R=12+, U=9, D=9 |
| ✓ PASS | Suite includes security tests | XSS, SQLi, unauthenticated, CORS |
| ✓ PASS | Suite includes performance benchmarks | API response times, page load, response sizes |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | QA dashboard loads | text_len=18085 |
| ✓ PASS | QA dashboard shows test count | has_pass=True |
| ✓ PASS | QA dashboard shows pass rate | has_pct=True |
| ✓ PASS | QA dashboard has charts | canvas=True |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Dup level valid: text-ratio-low.html | level=0 |
| ✓ PASS | Dup title is bool: text-ratio-low.html | type=bool |
| ✓ PASS | Dup level valid: canonical-other.html | level=0 |
| ✓ PASS | Dup title is bool: canonical-other.html | type=bool |
| ✓ PASS | Dup level valid: water-sports.html | level=0 |
| ✓ PASS | Dup title is bool: water-sports.html | type=bool |
| ✓ PASS | Dup level valid: gardening.html | level=0 |
| ✓ PASS | Dup title is bool: gardening.html | type=bool |
| ✓ PASS | Dup level valid: redirect-loop-target | level=0 |
| ✓ PASS | Dup title is bool: redirect-loop-target | type=bool |
| ✓ PASS | Dup level valid: homemade-pastry.html | level=0 |
| ✓ PASS | Dup title is bool: homemade-pastry.html | type=bool |
| ✓ PASS | Dup level valid: meta-desc-missing.ht | level=0 |
| ✓ PASS | Dup title is bool: meta-desc-missing.ht | type=bool |
| ✓ PASS | Dup level valid: meta-desc-duplicate- | level=1 |
| ✓ PASS | Dup title is bool: meta-desc-duplicate- | type=bool |
| ✓ PASS | Dup level valid: painting.html | level=0 |
| ✓ PASS | Dup title is bool: painting.html | type=bool |
| ✓ PASS | Dup level valid: root | level=0 |
| ✓ PASS | Dup title is bool: root | type=bool |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Found in crawl: text-ratio-low.html | found_in_crawl=True |
| ✓ PASS | Found in sitemaps type: text-ratio-low.html | found_in_sitemaps=False |
| ✓ PASS | Found in crawl: canonical-other.html | found_in_crawl=True |
| ✓ PASS | Found in sitemaps type: canonical-other.html | found_in_sitemaps=False |
| ✓ PASS | Found in crawl: water-sports.html | found_in_crawl=True |
| ✓ PASS | Found in sitemaps type: water-sports.html | found_in_sitemaps=False |
| ✓ PASS | Found in crawl: gardening.html | found_in_crawl=True |
| ✓ PASS | Found in sitemaps type: gardening.html | found_in_sitemaps=False |
| ✓ PASS | Found in crawl: redirect-loop-target | found_in_crawl=True |
| ✓ PASS | Found in sitemaps type: redirect-loop-target | found_in_sitemaps=False |
| ✓ PASS | Found in crawl: homemade-pastry.html | found_in_crawl=True |
| ✓ PASS | Found in sitemaps type: homemade-pastry.html | found_in_sitemaps=False |
| ✓ PASS | Found in crawl: meta-desc-missing.ht | found_in_crawl=True |
| ✓ PASS | Found in sitemaps type: meta-desc-missing.ht | found_in_sitemaps=False |
| ✓ PASS | Found in crawl: meta-desc-duplicate- | found_in_crawl=True |
| ✓ PASS | Found in sitemaps type: meta-desc-duplicate- | found_in_sitemaps=False |
| ✓ PASS | Found in crawl: painting.html | found_in_crawl=True |
| ✓ PASS | Found in sitemaps type: painting.html | found_in_sitemaps=False |
| ✓ PASS | Found in crawl: root | found_in_crawl=True |
| ✓ PASS | Found in sitemaps type: root | found_in_sitemaps=False |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | Hreflang count >= 0: text-ratio-low.html | count=0 |
| ✓ PASS | Hreflang count >= 0: canonical-other.html | count=0 |
| ✓ PASS | Hreflang count >= 0: water-sports.html | count=0 |
| ✓ PASS | Hreflang count >= 0: gardening.html | count=0 |
| ✓ PASS | Hreflang count >= 0: redirect-loop-target | count=0 |
| ✓ PASS | Hreflang count >= 0: homemade-pastry.html | count=0 |
| ✓ PASS | Hreflang count >= 0: meta-desc-missing.ht | count=0 |
| ✓ PASS | Hreflang count >= 0: meta-desc-duplicate- | count=0 |
| ✓ PASS | Hreflang count >= 0: painting.html | count=0 |
| ✓ PASS | Hreflang count >= 0: root | count=0 |
| Status | Test | Details |
|---|---|---|
| ✓ PASS | All/total_rows has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Security/url_http has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Security/url_https has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | URLs Containing Non-standard Characters/url_non_ascii has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | URLs Containing Non-standard Characters/url_underscores has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | URLs Containing Non-standard Characters/url_uppercases has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Status Codes/url_success has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Status Codes/url_redirect has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Status Codes/url_client_error has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Page Titles/url_title_missing has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Page Titles/url_title_duplicated has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Page Titles/url_title_long has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Status code by Depth/url_status_depth_1 has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Status code by Depth/url_status_depth_2 has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Status code by Depth/url_status_depth_3 has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Meta Description/url_meta_description has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Meta Description/url_meta_description has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Meta Description/url_meta_description has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | H1/url_h1_missing has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | H1/url_h1_duplicated has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | H1/url_h1_over_70 has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Canonical/url_canonical_presen has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Canonical/url_canonical_not_ma has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Canonical/url_canonical_missin has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Directives Robots/url_directive_index has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Directives Robots/url_directive_noinde has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Directives Robots/url_directive_follow has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Hreflang/url_hreflang_present has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Hreflang/url_hreflang_missing has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Pagination/url_paginated has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Images/url_images_missing has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Images/url_images_alt_missi has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | Performance/url_ttfb_over_600ms has required fields | keys=['filters', 'ident', 'title', 'urls'] |
| ✓ PASS | found_in_sitema bool: text-ratio-l | type=bool |
| ✓ PASS | found_only_in_s bool: text-ratio-l | type=bool |
| ✓ PASS | found_in_sitema bool: canonical-ot | type=bool |
| ✓ PASS | found_only_in_s bool: canonical-ot | type=bool |
| ✓ PASS | found_in_sitema bool: water-sports | type=bool |
| ✓ PASS | found_only_in_s bool: water-sports | type=bool |
| ✓ PASS | All 13 return 200 | 11/13 |
| ✓ PASS | Date minute: text-ratio | val=2026-04-03T13:5 |
| ✓ PASS | Date second: text-ratio | val=2026-04-03T13:5 |
| ✓ PASS | Date minute: canonical- | val=2026-04-03T13:5 |
| ✓ PASS | Date second: canonical- | val=2026-04-03T13:5 |
| ✓ PASS | Has goo: text-rat | |
| ✓ PASS | Has sea: text-rat | |
| ✓ PASS | Has goo: canonica | |
| ✓ PASS | Has sea: canonica | |
| ✓ PASS | Has goo: water-sp | |
| ✓ PASS | Has sea: water-sp |
| Timestamp | Pass Rate | Passed | Failed | URLs | Total Time | Crawl ID |
|---|---|---|---|---|---|---|
| 2026-04-08 04:12 UTC | 100.0% | 3868 | 1 | 190 | 10s | f86b84ae... |
| 2026-04-08 04:01 UTC | 100.0% | 3862 | 1 | 190 | 10s | f86b84ae... |
| 2026-04-08 03:51 UTC | 100.0% | 3856 | 1 | 190 | 10s | f86b84ae... |
| 2026-04-08 03:40 UTC | 100.0% | 3849 | 1 | 190 | 10s | f86b84ae... |
| 2026-04-08 03:30 UTC | 100.0% | 3849 | 1 | 190 | 10s | f86b84ae... |
| 2026-04-08 03:19 UTC | 100.0% | 3848 | 1 | 190 | 10s | f86b84ae... |
| 2026-04-08 03:08 UTC | 100.0% | 3847 | 1 | 190 | 10s | f86b84ae... |
| 2026-04-08 02:57 UTC | 100.0% | 3846 | 1 | 190 | 10s | f86b84ae... |
| 2026-04-08 02:47 UTC | 100.0% | 3845 | 1 | 190 | 39s | f86b84ae... |
| 2026-04-08 02:36 UTC | 100.0% | 3844 | 1 | 190 | 39s | f86b84ae... |
| 2026-04-08 02:25 UTC | 100.0% | 3843 | 1 | 190 | 39s | f86b84ae... |
| 2026-04-08 02:14 UTC | 100.0% | 3839 | 1 | 190 | 39s | f86b84ae... |
| 2026-04-08 02:03 UTC | 100.0% | 3837 | 1 | 190 | 39s | f86b84ae... |
| 2026-04-08 01:53 UTC | 100.0% | 3833 | 1 | 190 | 39s | f86b84ae... |
| 2026-04-08 01:42 UTC | 100.0% | 3832 | 1 | 190 | 3829s | f86b84ae... |
| 2026-04-08 01:32 UTC | 100.0% | 3827 | 1 | 190 | 3812s | f86b84ae... |
| 2026-04-08 01:21 UTC | 100.0% | 3824 | 1 | 190 | 3812s | f86b84ae... |
| 2026-04-08 01:10 UTC | 100.0% | 3819 | 1 | 190 | 3812s | f86b84ae... |
| 2026-04-08 01:00 UTC | 100.0% | 3815 | 1 | 190 | 3812s | f86b84ae... |
| 2026-04-08 00:49 UTC | 100.0% | 3812 | 1 | 190 | 3812s | f86b84ae... |
| 2026-04-08 00:39 UTC | 100.0% | 3807 | 1 | 190 | 3748s | f86b84ae... |
| 2026-04-08 00:23 UTC | 100.0% | 3804 | 1 | 190 | 3748s | f86b84ae... |
| 2026-04-08 00:13 UTC | 100.0% | 3799 | 1 | 190 | 3748s | f86b84ae... |
| 2026-04-07 23:58 UTC | 100.0% | 3794 | 1 | 190 | 3748s | f86b84ae... |
| 2026-04-07 23:48 UTC | 100.0% | 3791 | 1 | 190 | 3748s | f86b84ae... |
| 2026-04-07 23:28 UTC | 100.0% | 3782 | 1 | 190 | 3748s | f86b84ae... |
| 2026-04-07 23:18 UTC | 100.0% | 3777 | 1 | 190 | 3748s | f86b84ae... |
| 2026-04-07 23:07 UTC | 100.0% | 3772 | 1 | 190 | 3748s | f86b84ae... |
| 2026-04-07 22:57 UTC | 100.0% | 3763 | 1 | 190 | 3748s | f86b84ae... |
| 2026-04-07 22:47 UTC | 100.0% | 3760 | 1 | 190 | 3748s | f86b84ae... |
| 2026-04-07 22:36 UTC | 100.0% | 3754 | 1 | 190 | 3748s | f86b84ae... |
| 2026-04-07 22:21 UTC | 100.0% | 3751 | 1 | 190 | 3748s | f86b84ae... |
| 2026-04-07 22:10 UTC | 100.0% | 3746 | 1 | 190 | 10s | f86b84ae... |
| 2026-04-07 22:00 UTC | 100.0% | 3741 | 1 | 190 | 10s | f86b84ae... |
| 2026-04-07 21:50 UTC | 100.0% | 3735 | 1 | 190 | 10s | f86b84ae... |
| 2026-04-07 21:39 UTC | 100.0% | 3731 | 1 | 190 | 10s | f86b84ae... |
| 2026-04-07 21:29 UTC | 100.0% | 3725 | 1 | 190 | 10s | f86b84ae... |
| 2026-04-07 21:18 UTC | 100.0% | 3719 | 1 | 190 | 10s | f86b84ae... |
| 2026-04-07 21:08 UTC | 100.0% | 3711 | 1 | 190 | 10s | f86b84ae... |
| 2026-04-07 20:57 UTC | 100.0% | 3703 | 1 | 190 | 8s | f86b84ae... |
| 2026-04-07 20:46 UTC | 100.0% | 3696 | 1 | 190 | 8s | f86b84ae... |
| 2026-04-07 20:31 UTC | 100.0% | 3689 | 1 | 190 | 8s | f86b84ae... |
| 2026-04-07 20:11 UTC | 100.0% | 3682 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 20:00 UTC | 100.0% | 3677 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 19:50 UTC | 100.0% | 3669 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 19:34 UTC | 100.0% | 3663 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 19:24 UTC | 100.0% | 3656 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 19:09 UTC | 100.0% | 3649 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 18:58 UTC | 100.0% | 3644 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 18:48 UTC | 100.0% | 3637 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 18:37 UTC | 100.0% | 3631 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 18:18 UTC | 100.0% | 3625 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 18:07 UTC | 100.0% | 3619 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 17:57 UTC | 100.0% | 3612 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 17:47 UTC | 100.0% | 3603 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 17:37 UTC | 100.0% | 3598 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 17:22 UTC | 100.0% | 3590 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 17:12 UTC | 100.0% | 3583 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 16:57 UTC | 100.0% | 3575 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 16:47 UTC | 100.0% | 3565 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 16:28 UTC | 100.0% | 3558 | 1 | 190 | 8s | f86b84ae... |
| 2026-04-07 16:18 UTC | 100.0% | 3553 | 1 | 190 | 8s | f86b84ae... |
| 2026-04-07 16:08 UTC | 100.0% | 3548 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 15:53 UTC | 100.0% | 3539 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 15:44 UTC | 100.0% | 3531 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 15:34 UTC | 100.0% | 3521 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 15:24 UTC | 100.0% | 3509 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 15:10 UTC | 100.0% | 3500 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 15:00 UTC | 100.0% | 3490 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 14:51 UTC | 100.0% | 3482 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 14:41 UTC | 100.0% | 3477 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 14:31 UTC | 100.0% | 3473 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 14:21 UTC | 100.0% | 3470 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 14:12 UTC | 100.0% | 3468 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 14:02 UTC | 100.0% | 3462 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 13:53 UTC | 100.0% | 3458 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 13:30 UTC | 100.0% | 3394 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 13:20 UTC | 100.0% | 3386 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 13:10 UTC | 100.0% | 3383 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 12:59 UTC | 100.0% | 3379 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 12:49 UTC | 100.0% | 3378 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 12:38 UTC | 100.0% | 3372 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 12:28 UTC | 100.0% | 3372 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 12:17 UTC | 100.0% | 3372 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 12:07 UTC | 100.0% | 3362 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 11:57 UTC | 100.0% | 3352 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 11:47 UTC | 100.0% | 3348 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 11:37 UTC | 100.0% | 3345 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 11:26 UTC | 100.0% | 3336 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 11:16 UTC | 100.0% | 3306 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 11:06 UTC | 100.0% | 3306 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 10:49 UTC | 100.0% | 3294 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 10:39 UTC | 100.0% | 3289 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 10:28 UTC | 100.0% | 3285 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 10:19 UTC | 100.0% | 3281 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 10:08 UTC | 100.0% | 3278 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 09:58 UTC | 100.0% | 3266 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 09:48 UTC | 100.0% | 3245 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 09:38 UTC | 100.0% | 3225 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 09:23 UTC | 100.0% | 3205 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 09:14 UTC | 100.0% | 3195 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 09:03 UTC | 100.0% | 3175 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 08:53 UTC | 100.0% | 3152 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 08:44 UTC | 100.0% | 3122 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 08:34 UTC | 100.0% | 3102 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 08:24 UTC | 100.0% | 3072 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 08:14 UTC | 100.0% | 3042 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 08:04 UTC | 100.0% | 3022 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 07:54 UTC | 100.0% | 2951 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 07:44 UTC | 100.0% | 2911 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 07:34 UTC | 100.0% | 2881 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 07:25 UTC | 100.0% | 2857 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 07:15 UTC | 100.0% | 2834 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 07:05 UTC | 100.0% | 2804 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 06:55 UTC | 100.0% | 2784 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 06:46 UTC | 100.0% | 2764 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 06:36 UTC | 100.0% | 2753 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 06:26 UTC | 100.0% | 2741 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 06:16 UTC | 100.0% | 2735 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 06:06 UTC | 100.0% | 2728 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 05:56 UTC | 100.0% | 2721 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 05:46 UTC | 100.0% | 2718 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 05:36 UTC | 100.0% | 2714 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 05:27 UTC | 100.0% | 2709 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 05:17 UTC | 100.0% | 2706 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 05:07 UTC | 100.0% | 2703 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 04:58 UTC | 100.0% | 2700 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 04:32 UTC | 100.0% | 2671 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 04:22 UTC | 100.0% | 2661 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 04:13 UTC | 100.0% | 2652 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 04:03 UTC | 100.0% | 2643 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 03:53 UTC | 100.0% | 2639 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 03:44 UTC | 100.0% | 2635 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 03:34 UTC | 100.0% | 2628 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 03:25 UTC | 100.0% | 2626 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 03:16 UTC | 100.0% | 2626 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 03:06 UTC | 100.0% | 2613 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 02:57 UTC | 100.0% | 2610 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 02:47 UTC | 100.0% | 2604 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 02:38 UTC | 100.0% | 2604 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 02:28 UTC | 100.0% | 2598 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 02:19 UTC | 100.0% | 2598 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 02:09 UTC | 100.0% | 2592 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 01:52 UTC | 100.0% | 2581 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 01:42 UTC | 100.0% | 2581 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 01:33 UTC | 100.0% | 2576 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 01:23 UTC | 100.0% | 2567 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 01:14 UTC | 100.0% | 2563 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 01:04 UTC | 100.0% | 2560 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 00:55 UTC | 100.0% | 2554 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 00:45 UTC | 100.0% | 2541 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 00:36 UTC | 100.0% | 2528 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 00:27 UTC | 100.0% | 2523 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 00:17 UTC | 100.0% | 2508 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-07 00:07 UTC | 100.0% | 2500 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 23:54 UTC | 100.0% | 2500 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 23:40 UTC | 100.0% | 2500 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 23:30 UTC | 100.0% | 2488 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 23:17 UTC | 100.0% | 2475 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 23:08 UTC | 100.0% | 2460 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 22:59 UTC | 100.0% | 2459 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 22:41 UTC | 100.0% | 2456 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 22:32 UTC | 100.0% | 2451 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 22:22 UTC | 100.0% | 2405 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 22:13 UTC | 100.0% | 2398 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 22:04 UTC | 100.0% | 2383 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 21:54 UTC | 100.0% | 2381 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 21:45 UTC | 100.0% | 2376 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 21:36 UTC | 100.0% | 2368 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 21:26 UTC | 100.0% | 2365 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 21:17 UTC | 100.0% | 2358 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 21:08 UTC | 100.0% | 2346 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 20:58 UTC | 100.0% | 2342 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 20:49 UTC | 100.0% | 2337 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 20:40 UTC | 100.0% | 2330 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 20:31 UTC | 100.0% | 2315 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 20:22 UTC | 100.0% | 2310 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 20:12 UTC | 100.0% | 2310 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 20:03 UTC | 100.0% | 2300 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 19:54 UTC | 100.0% | 2295 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 19:45 UTC | 100.0% | 2288 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 19:36 UTC | 100.0% | 2276 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 19:27 UTC | 100.0% | 2262 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 19:18 UTC | 100.0% | 2244 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 19:08 UTC | 100.0% | 2130 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 18:59 UTC | 100.0% | 2127 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 18:50 UTC | 100.0% | 2120 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 18:40 UTC | 100.0% | 2116 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 18:31 UTC | 100.0% | 2110 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 18:22 UTC | 100.0% | 2104 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 18:04 UTC | 100.0% | 2095 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 17:55 UTC | 100.0% | 2071 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 17:46 UTC | 100.0% | 2066 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 17:37 UTC | 100.0% | 2056 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 17:28 UTC | 100.0% | 2049 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 17:19 UTC | 100.0% | 2030 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 17:10 UTC | 100.0% | 2014 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 17:01 UTC | 100.0% | 2002 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 16:52 UTC | 100.0% | 2002 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 16:42 UTC | 100.0% | 1981 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 16:24 UTC | 100.0% | 1959 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 16:15 UTC | 100.0% | 1936 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 16:06 UTC | 100.0% | 1865 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 15:57 UTC | 100.0% | 1861 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 15:48 UTC | 100.0% | 1855 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 15:39 UTC | 100.0% | 1852 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 15:30 UTC | 100.0% | 1839 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 15:14 UTC | 100.0% | 1818 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 15:05 UTC | 100.0% | 1786 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 14:48 UTC | 100.0% | 1707 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 14:39 UTC | 100.0% | 1660 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 14:31 UTC | 100.0% | 1648 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 14:21 UTC | 100.0% | 1648 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 14:12 UTC | 100.0% | 1639 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 14:03 UTC | 100.0% | 1631 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 13:55 UTC | 100.0% | 1627 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 13:46 UTC | 100.0% | 1581 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 13:37 UTC | 100.0% | 1571 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 13:28 UTC | 100.0% | 1553 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 13:19 UTC | 100.0% | 1549 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 13:10 UTC | 100.0% | 1532 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 13:02 UTC | 100.0% | 1309 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 12:53 UTC | 100.0% | 1296 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 12:48 UTC | 100.0% | 1296 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 12:39 UTC | 100.0% | 1290 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 12:31 UTC | 100.0% | 1246 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 12:22 UTC | 100.0% | 1241 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 12:13 UTC | 100.0% | 1234 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 12:05 UTC | 100.0% | 1226 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 11:56 UTC | 100.0% | 1226 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 11:47 UTC | 100.0% | 1211 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 11:38 UTC | 100.0% | 1181 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 11:29 UTC | 100.0% | 1153 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 11:20 UTC | 100.0% | 1132 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 11:12 UTC | 100.0% | 1124 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 11:03 UTC | 100.0% | 1115 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 10:54 UTC | 100.0% | 1110 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 10:46 UTC | 100.0% | 1088 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 10:37 UTC | 100.0% | 1082 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 10:29 UTC | 100.0% | 1075 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 10:20 UTC | 100.0% | 1031 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 10:11 UTC | 100.0% | 1022 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 10:03 UTC | 100.0% | 1015 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 09:46 UTC | 100.0% | 1009 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 09:37 UTC | 100.0% | 1009 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 09:28 UTC | 100.0% | 975 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 09:19 UTC | 100.0% | 935 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 09:10 UTC | 100.0% | 929 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 09:01 UTC | 100.0% | 921 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 08:52 UTC | 100.0% | 913 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 08:35 UTC | 100.0% | 903 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 08:19 UTC | 100.0% | 897 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 08:03 UTC | 100.0% | 887 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 07:47 UTC | 100.0% | 844 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 07:38 UTC | 100.0% | 820 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 07:30 UTC | 100.0% | 820 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 07:18 UTC | 100.0% | 806 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 07:09 UTC | 100.0% | 787 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 07:01 UTC | 100.0% | 783 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 06:53 UTC | 100.0% | 775 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 06:46 UTC | 100.0% | 768 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 06:37 UTC | 100.0% | 755 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 06:30 UTC | 100.0% | 745 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 06:22 UTC | 100.0% | 731 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 06:12 UTC | 100.0% | 684 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 05:30 UTC | 100.0% | 671 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 05:20 UTC | 100.0% | 664 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 05:00 UTC | 100.0% | 667 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-06 04:53 UTC | 100.0% | 653 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-05 19:56 UTC | 100.0% | 626 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-05 18:04 UTC | 100.0% | 603 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-04 07:16 UTC | 100.0% | 594 | 1 | 190 | 190s | f86b84ae... |
| 2026-04-03 14:49 UTC | 97.3% | 142 | 4 | 190 | 178s | 461d879a... |
| 2026-04-03 14:19 UTC | 98.0% | 145 | 3 | 190 | 289s | f86b84ae... |