chore: 更新项目文档与忽略规则,移除旧镜像归档跟踪
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+13
-4
@@ -8,6 +8,7 @@ instance/
|
||||
.pytest_cache/
|
||||
.mypy_cache/
|
||||
user_input/
|
||||
# Note: user_input/广寒计划/Timeline.md is intentionally tracked (mission timeline source)
|
||||
|
||||
# Temp files
|
||||
~$*
|
||||
@@ -20,19 +21,27 @@ scripts/update_vulture_wiki.py
|
||||
# Reference docs & reports
|
||||
docs/
|
||||
|
||||
# Tests (E2E/screenshot-based)
|
||||
tests/
|
||||
# Local E2E tests remain ignored; keep new regression coverage trackable.
|
||||
tests/*
|
||||
!tests/test_first_stage_fixes.py
|
||||
|
||||
# Demo screenshots
|
||||
demo/test_screenshots/
|
||||
|
||||
# Playwright output and logs
|
||||
output/playwright/
|
||||
# Generated output: deployment bundles, screenshot caches, Playwright logs
|
||||
output/
|
||||
.playwright-cli/
|
||||
.superpowers/
|
||||
|
||||
# KSP screenshots and upload cache
|
||||
data/KSP/
|
||||
|
||||
# Docker image archives (rebuilt per release; deploy docs are the source of truth)
|
||||
docker_image/*.tar
|
||||
docker_image/*.tar.gz
|
||||
|
||||
# Verification screenshots
|
||||
artifacts/
|
||||
|
||||
# Misc
|
||||
test1
|
||||
|
||||
@@ -43,6 +43,18 @@ python scripts/wiki_update_page.py --path "home/Space_Shuttles/Vulture_Shuttle"
|
||||
|
||||
Add `--create-if-missing` for new pages. HTML files use `<style>` blocks (extracted to page CSS) and wiki-infobox table patterns. Images use `<div class="image-placeholder">` when the actual render is not yet available.
|
||||
|
||||
### Mission gallery release gate (mandatory)
|
||||
|
||||
- Mission gallery interaction is provided only by `scripts/wiki_mission_gallery_runtime.js`, published with `scripts/wiki_publish_mission_gallery_runtime.py`. Do not put gallery JavaScript in page `scriptJs` or inline `<script>` blocks.
|
||||
- After any gallery content, CSS, runtime, or publishing change, run both commands below. A gallery release is incomplete if either command fails or if screenshots were not visually inspected:
|
||||
|
||||
```powershell
|
||||
py -3.12 -m pytest tests/test_mission_galleries.py -q -p no:cacheprovider
|
||||
py -3.12 scripts/wiki_verify_mission_galleries.py --screenshot-dir output/playwright/mission-gallery-e2e
|
||||
```
|
||||
|
||||
- The E2E gate must cover every mission gallery, not a sample. See `docs/wiki-mission-gallery-quality-gate.md` for the incident root cause and checklist.
|
||||
|
||||
## Database
|
||||
|
||||
- PostgreSQL is authoritative for mission logs and timelines
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -14,9 +14,10 @@ MOON_PERIOD = timedelta(days=27.32166) # 月球自转周期
|
||||
TRANSIT_DAYS = timedelta(days=5) # 地月转移耗时
|
||||
|
||||
# 参考着陆窗口(UTC)—— 发射窗口 = 着陆窗口 − 5 天地月转移
|
||||
# 基于 2024 年 10 月精确窗口校准
|
||||
REF_LANDING_A = datetime(2024, 10, 21, 4, 55, tzinfo=timezone.utc)
|
||||
REF_LANDING_B = datetime(2024, 10, 29, 3, 15, tzinfo=timezone.utc)
|
||||
# A窗: 2025-05-14 04:09 着陆 校准 (k=8)
|
||||
# B窗: 2025-03-15 02:22 着陆 校准 (k=5)
|
||||
REF_LANDING_A = datetime(2024, 10, 7, 14, 23, tzinfo=timezone.utc)
|
||||
REF_LANDING_B = datetime(2024, 10, 29, 11, 46, tzinfo=timezone.utc)
|
||||
REF_LAUNCH_A = REF_LANDING_A - TRANSIT_DAYS
|
||||
REF_LAUNCH_B = REF_LANDING_B - TRANSIT_DAYS
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import tempfile
|
||||
import unittest
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
from openpyxl import Workbook
|
||||
|
||||
from app import create_app
|
||||
from app.config import Config
|
||||
from app.extensions import db
|
||||
from app.models import Asset, AssetLogEntry, SimulationSetting
|
||||
from app.services.log_book_importer import import_log_book_data
|
||||
|
||||
|
||||
class TestConfig(Config):
|
||||
TESTING = True
|
||||
SECRET_KEY = "test"
|
||||
SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:"
|
||||
|
||||
|
||||
class FirstStageFixTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.app = create_app(TestConfig)
|
||||
self.client = self.app.test_client()
|
||||
with self.app.app_context():
|
||||
db.create_all()
|
||||
|
||||
def tearDown(self) -> None:
|
||||
with self.app.app_context():
|
||||
db.session.remove()
|
||||
db.drop_all()
|
||||
|
||||
def test_log_book_import_uses_state_nodes_and_imports_sub_log_events(self) -> None:
|
||||
workbook = Workbook()
|
||||
sheet = workbook.active
|
||||
sheet.title = "ST-01"
|
||||
sheet.append(["Log", "Start", "End", "Detail", "Sub log", None, None, "Location"])
|
||||
sheet.append(
|
||||
[
|
||||
"Mars transfer",
|
||||
datetime(2060, 1, 1),
|
||||
datetime(2060, 1, 10),
|
||||
"Outbound mission",
|
||||
"2060-01-02 Reached Mars orbit",
|
||||
None,
|
||||
None,
|
||||
"LEO",
|
||||
]
|
||||
)
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
path = Path(temp_dir) / "log_book.xlsx"
|
||||
workbook.save(path)
|
||||
with self.app.app_context():
|
||||
summary = import_log_book_data(path)
|
||||
entries = db.session.query(AssetLogEntry).order_by(AssetLogEntry.start_at).all()
|
||||
|
||||
self.assertEqual(summary.imported_assets[0].state_entries, 1)
|
||||
self.assertEqual(summary.imported_assets[0].event_entries, 1)
|
||||
self.assertEqual([entry.entry_kind for entry in entries], ["state", "event"])
|
||||
self.assertEqual(entries[0].state_nodes[0].target_location, "LEO")
|
||||
self.assertEqual(entries[0].state_nodes[0].state_label, "Mars transfer")
|
||||
self.assertEqual(entries[1].state_nodes[0].target_location, "Mars")
|
||||
|
||||
def test_dashboard_attributes_only_events_within_thirty_days(self) -> None:
|
||||
with self.app.app_context():
|
||||
db.session.add(SimulationSetting(key="current_time", value="2060-03-12T09:00"))
|
||||
alpha = Asset(name="Alpha", asset_type="Vehicle")
|
||||
beta = Asset(name="Beta", asset_type="Vehicle")
|
||||
alpha.log_entries.extend(
|
||||
[
|
||||
AssetLogEntry(
|
||||
entry_kind="event",
|
||||
title="Alpha event",
|
||||
start_at=datetime(2060, 3, 20, tzinfo=timezone.utc),
|
||||
),
|
||||
AssetLogEntry(
|
||||
entry_kind="event",
|
||||
title="Too far away",
|
||||
start_at=datetime(2060, 5, 1, tzinfo=timezone.utc),
|
||||
),
|
||||
]
|
||||
)
|
||||
beta.log_entries.append(
|
||||
AssetLogEntry(
|
||||
entry_kind="event",
|
||||
title="Beta event",
|
||||
start_at=datetime(2060, 3, 21, tzinfo=timezone.utc),
|
||||
)
|
||||
)
|
||||
db.session.add_all([alpha, beta])
|
||||
db.session.commit()
|
||||
|
||||
response = self.client.get("/")
|
||||
page = response.get_data(as_text=True)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn("Alpha: Alpha event", page)
|
||||
self.assertIn("Beta: Beta event", page)
|
||||
self.assertNotIn("Beta: Alpha event", page)
|
||||
self.assertNotIn("Too far away", page)
|
||||
|
||||
def test_simulation_time_endpoint_persists_through_model(self) -> None:
|
||||
response = self.client.post(
|
||||
"/api/v1/sim-time",
|
||||
json={"sim_time": "2061-04-05T06:07"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
with self.app.app_context():
|
||||
setting = db.session.get(SimulationSetting, "current_time")
|
||||
self.assertIsNotNone(setting)
|
||||
self.assertEqual(setting.value, "2061-04-05T06:07")
|
||||
|
||||
def test_location_and_timeline_json_escape_script_terminators(self) -> None:
|
||||
payload = "Probe </script><script>alert(1)</script>"
|
||||
with self.app.app_context():
|
||||
asset = Asset(name=payload, asset_type="Station", home_region="LEO")
|
||||
asset.log_entries.append(
|
||||
AssetLogEntry(
|
||||
entry_kind="event",
|
||||
title=payload,
|
||||
start_at=datetime(2060, 3, 1, tzinfo=timezone.utc),
|
||||
)
|
||||
)
|
||||
db.session.add(asset)
|
||||
db.session.commit()
|
||||
|
||||
for path in ("/mission-preview/location-board", "/mission-preview/timeline"):
|
||||
response = self.client.get(path)
|
||||
page = response.get_data(as_text=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertNotIn(payload, page)
|
||||
self.assertIn("\\u003c/script\\u003e", page)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user