deploy ProspectOS workspace v5 final

This commit is contained in:
Marco0300
2026-09-07 10:23:38 +02:00
parent e27a003703
commit 181b83fc5e
8 changed files with 649 additions and 17 deletions
+89 -1
View File
@@ -201,6 +201,92 @@ class BrowserOnlyAppTests(unittest.TestCase):
self.assertIn("Services", body)
self.assertIn("images.unsplash.com", body)
self.assertIn("https://images.unsplash.com", response.getheader("Content-Security-Policy"))
self.assertIn("frame-ancestors 'none'", response.getheader("Content-Security-Policy"))
def test_mockup_editor_creates_a_new_version_and_keeps_original(self):
conn = server.db()
conn.execute("INSERT INTO prospects(name,domain,category,discovered_at) VALUES(?,?,?,?)", ("Brightspark Electrical", "maps:bright-editor", "electrician", server.now()))
conn.commit(); conn.close()
status, original = self.request("POST", "/api/prospects/1/mockup", {"mode": "multipage"})
self.assertEqual(status, 201)
self.assertEqual(original.get("version"), None) # v1 rows remain backwards-compatible
conn = server.db()
for key, value in (("AI_PROVIDER", "nous"), ("AI_MODEL", "meituan/longcat-2.0:free"), ("AI_API_KEY", "test-key")):
conn.execute("INSERT INTO settings(key,value,updated_at) VALUES(?,?,?)", (key, value, server.now()))
conn.commit(); conn.close()
ai_response = {"choices": [{"message": {"content": json.dumps({
"tagline": "Powerful work. Presented with confidence.",
"intro": "A sharper digital home for Brightspark Electrical.",
"services": [{"name": "Electrical services", "description": "A clear way to understand the work available."}],
"about": "A refined concept based on the public business listing.",
"cta": "Let’s make the next step easy.",
"hero_image": server.STOCK_IMAGE_LIBRARY["electric"][0],
"gallery_image": server.STOCK_IMAGE_LIBRARY["electric"][1],
})}}]}
with patch("api.server.http_json", return_value=ai_response) as request:
status, edited = self.request("POST", f"/api/mockups/{original['id']}/edit", {"instruction": "Make the whole site darker and put more emphasis on services.", "page": "all"})
self.assertEqual(status, 201)
self.assertEqual(edited["version"], 2)
self.assertEqual(edited["parent_id"], original["id"])
self.assertEqual(edited["mode"], "multipage")
self.assertIn("Applied your request", edited["change_summary"])
self.assertIn("max_tokens", request.call_args.args[2])
self.assertEqual(request.call_args.args[2]["max_tokens"], 1800)
self.conn.request("GET", "/mockups/" + edited["token"] + "/services?embed=1")
response = self.conn.getresponse(); body = response.read().decode("utf-8")
self.assertEqual(response.status, 200)
self.assertIn("frame-ancestors 'self'", response.getheader("Content-Security-Policy"))
self.assertIn("--paper:#091321", body)
self.assertIn("Electrical services", body)
self.conn.request("GET", "/mockups/" + edited["token"])
response = self.conn.getresponse(); edited_home = response.read().decode("utf-8")
self.assertEqual(response.status, 200)
self.assertIn("Powerful work", edited_home)
self.conn.request("GET", "/mockups/" + original["token"] + "/services")
response = self.conn.getresponse(); original_body = response.read().decode("utf-8")
self.assertEqual(response.status, 200)
self.assertIn("Brightspark Electrical", original_body)
status, metadata = self.request("GET", f"/api/mockups/{edited['id']}")
self.assertEqual(status, 200)
self.assertEqual(metadata["version"], 2)
status, listed = self.request("GET", "/api/prospects/1/mockups")
self.assertEqual(status, 200)
self.assertEqual([item["version"] for item in listed["items"]], [2, 1])
def test_multipage_ai_uses_compact_content_and_renders_all_pages(self):
conn = server.db()
conn.execute("INSERT INTO prospects(name,domain,category,discovered_at) VALUES(?,?,?,?)", ("Brightspark Electrical", "maps:bright-ai", "electrician", server.now()))
for key, value in (("AI_PROVIDER", "nous"), ("AI_MODEL", "meituan/longcat-2.0:free"), ("AI_API_KEY", "test-key")):
conn.execute("INSERT INTO settings(key,value,updated_at) VALUES(?,?,?)", (key, value, server.now()))
conn.commit(); conn.close()
response = {"choices": [{"message": {"content": json.dumps({
"tagline": "Powerful work. Presented with clarity.",
"intro": "A considered digital home for Brightspark Electrical.",
"services": [{"name": "Electrical services", "description": "A clear way to understand the work available."}],
"about": "A refined concept grounded in the public business listing.",
"cta": "Let’s make the next step easy.",
"hero_image": server.STOCK_IMAGE_LIBRARY["electric"][0],
"gallery_image": server.STOCK_IMAGE_LIBRARY["electric"][1],
})}}]}
with patch("api.server.http_json", return_value=response) as request:
status, created = self.request("POST", "/api/prospects/1/mockup", {"mode": "multipage"})
self.assertEqual(status, 201)
self.assertEqual(created["generation_source"], "ai")
self.assertEqual({item["slug"] for item in created["pages"]}, {"home", "services", "about", "contact"})
payload = request.call_args.args[2]
self.assertEqual(payload["max_tokens"], 1800)
self.assertNotIn("pages", json.dumps(payload["messages"]))
stored = server.db().execute("SELECT pages_json FROM mockups WHERE id=?", (created["id"],)).fetchone()
pages = json.loads(stored[0])
self.assertIn("Powerful work", pages["home"])
self.assertTrue(all("Brightspark Electrical" in html and "@keyframes" in html and "<footer" in html for html in pages.values()))
def test_onepage_mockup_uses_in_page_navigation(self):
html = server.premium_render_pages({"name": "Acme", "category": "plumber"}, mode="onepage")["home"]
self.assertIn('href="#services"', html)
self.assertIn('id="services"', html)
self.assertIn('id="about"', html)
self.assertIn('id="contact"', html)
def test_browser_investigator_normalises_and_filters_candidates(self):
raw = '{"candidates":[{"url":"https://www.acme.co.za/about","confidence":88,"evidence":["name match"]},{"url":"https://facebook.com/acme","confidence":99},{"domain":"other.co.za","confidence":"42","evidence":"not a list"}]}'
@@ -314,18 +400,20 @@ class BrowserOnlyAppTests(unittest.TestCase):
status, body = self.request("POST", "/api/settings", {
"browser_use": {"enabled": True, "max_steps": 22, "timeout_seconds": 900, "llm_timeout_seconds": 120},
"maps": {"request_timeout_seconds": 1800, "poll_seconds": 4, "max_depth": 3},
"limits": {"website_scan_timeout_seconds": 15},
"limits": {"website_scan_timeout_seconds": 15, "ai_mockup_timeout_seconds": 75},
})
self.assertEqual(status, 200)
self.assertEqual(body["browser_use"]["timeout_seconds"], 900)
self.assertEqual(body["maps"]["request_timeout_seconds"], 1800)
self.assertEqual(body["limits"]["website_scan_timeout_seconds"], 15)
self.assertEqual(body["limits"]["ai_mockup_timeout_seconds"], 75)
status, settings = self.request("GET", "/api/settings")
self.assertEqual(status, 200)
self.assertEqual(settings["browser_use"]["max_steps"], 22)
self.assertEqual(settings["browser_use"]["llm_timeout_seconds"], 120)
self.assertEqual(settings["maps"]["poll_seconds"], 4.0)
self.assertEqual(settings["maps"]["max_depth"], 3)
self.assertEqual(settings["limits"]["ai_mockup_timeout_seconds"], 75)
self.assertEqual(server.browser_use_limits(), (22, 900))
self.assertEqual(server.website_scan_timeout_seconds(), 15)