build: add -ldl linker flag, recursive source discovery, and API test targets to Makefile

This commit is contained in:
2026-02-07 12:04:52 +00:00
parent 8e86b2d106
commit cd9e55dc64
181 changed files with 20646 additions and 7198 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+31
View File
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>About - Secure Web Project</title>
<link rel="stylesheet" href="css/style.css">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self';">
</head>
<body>
<header>
<h1>About Our Project</h1>
<nav>
<a href="index.html">Home</a>
<a href="about.html">About</a>
<a href="contact.html">Contact</a>
</nav>
</header>
<main>
<section>
<h2>Our Mission</h2>
<p>To develop secure, professional, and well-structured web applications.</p>
</section>
</main>
<footer>
<p>&copy; 2026 Secure Web Inc.</p>
</footer>
<script src="js/script.js"></script>
</body>
</html>
+42
View File
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact - Secure Web Project</title>
<link rel="stylesheet" href="css/style.css">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self';">
</head>
<body>
<header>
<h1>Contact Us</h1>
<nav>
<a href="index.html">Home</a>
<a href="about.html">About</a>
<a href="contact.html">Contact</a>
</nav>
</header>
<main>
<section>
<h2>Get in Touch</h2>
<form action="#" method="post">
<label for="name">Name:</label>
<input type="text" id="name" name="name" required>
<br>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<br>
<label for="message">Message:</label>
<textarea id="message" name="message" required></textarea>
<br>
<button type="submit">Send</button>
</form>
</section>
</main>
<footer>
<p>&copy; 2026 Secure Web Inc.</p>
</footer>
<script src="js/script.js"></script>
</body>
</html>
+63
View File
@@ -0,0 +1,63 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
header {
background-color: #333;
color: #fff;
padding: 10px 20px;
}
header h1 {
margin: 0;
}
nav a {
color: #fff;
margin: 0 10px;
text-decoration: none;
}
nav a:hover {
text-decoration: underline;
}
main {
padding: 20px;
}
footer {
background-color: #333;
color: #fff;
text-align: center;
padding: 10px 20px;
position: fixed;
bottom: 0;
width: 100%;
}
form {
background-color: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
label {
display: block;
margin-top: 10px;
}
input, textarea {
width: 100%;
padding: 8px;
margin-top: 5px;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
margin-top: 10px;
padding: 10px 15px;
background-color: #333;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #555;
}
Binary file not shown.
+31
View File
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home - Secure Web Project</title>
<link rel="stylesheet" href="css/style.css">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self';">
</head>
<body>
<header>
<h1>Welcome to Our Secure Web Project</h1>
<nav>
<a href="index.html">Home</a>
<a href="about.html">About</a>
<a href="contact.html">Contact</a>
</nav>
</header>
<main>
<section>
<h2>Introduction</h2>
<p>This project follows best practices for security, structure, and professionalism.</p>
</section>
</main>
<footer>
<p>&copy; 2026 Secure Web Inc.</p>
</footer>
<script src="js/script.js"></script>
</body>
</html>
+9
View File
@@ -0,0 +1,9 @@
// JavaScript for enhanced security and interactivity
// Example: Prevent form submission for demonstration
document.querySelectorAll('form').forEach(form => {
form.addEventListener('submit', function(event) {
event.preventDefault();
alert('Form submission is disabled for security reasons.');
});
});
+3
View File
@@ -0,0 +1,3 @@
Extracted email addresses: test@example.com
Identified words: hellohello, world, line1
Original input: hellohello worldtest@example.comline1.
+63
View File
@@ -0,0 +1,63 @@
/* Basic Reset and Typography */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
line-height: 1.6;
background-color: #f4f4f4;
color: #333;
}
header {
background-color: #333;
color: #fff;
padding: 20px 0;
text-align: center;
}
header h1 {
margin: 0;
}
nav ul {
list-style: none;
padding: 0;
}
nav ul li {
display: inline;
margin: 0 15px;
}
nav ul li a {
color: #fff;
text-decoration: none;
}
nav ul li a:hover {
text-decoration: underline;
}
main {
padding: 20px;
max-width: 1200px;
margin: auto;
}
footer {
background-color: #333;
color: #fff;
text-align: center;
padding: 10px 0;
position: fixed;
width: 100%;
bottom: 0;
}
/* Responsive Design */
@media (max-width: 768px) {
nav ul li {
display: block;
margin: 10px 0;
}
}
+9
View File
@@ -0,0 +1,9 @@
// Main JavaScript file for future enhancements
// Example: Smooth scroll for navigation links
document.querySelectorAll('nav a').forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({ behavior: 'smooth' });
});
});
+32
View File
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>About Us</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<header>
<h1>About Us</h1>
<nav>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="about.html">About</a></li>
<li><a href="services.html">Services</a></li>
<li><a href="contact.html">Contact</a></li>
</ul>
</nav>
</header>
<main>
<section>
<h2>Our Story</h2>
<p>This page provides information about our company and team.</p>
</section>
</main>
<footer>
<p>&copy; 2026 Our Company</p>
</footer>
<script src="../js/main.js"></script>
</body>
</html>
+32
View File
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact Us</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<header>
<h1>Contact Us</h1>
<nav>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="about.html">About</a></li>
<li><a href="services.html">Services</a></li>
<li><a href="contact.html">Contact</a></li>
</ul>
</nav>
</header>
<main>
<section>
<h2>Get in Touch</h2>
<p>This page provides contact information and a contact form.</p>
</section>
</main>
<footer>
<p>&copy; 2026 Our Company</p>
</footer>
<script src="../js/main.js"></script>
</body>
</html>
+32
View File
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home Page</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<header>
<h1>Welcome to Our Multi-Page Website</h1>
<nav>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="about.html">About</a></li>
<li><a href="services.html">Services</a></li>
<li><a href="contact.html">Contact</a></li>
</ul>
</nav>
</header>
<main>
<section>
<h2>Introduction</h2>
<p>This is the homepage of our multi-page website, built with best practices in mind.</p>
</section>
</main>
<footer>
<p>&copy; 2026 Our Company</p>
</footer>
<script src="../js/main.js"></script>
</body>
</html>
+32
View File
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Services</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<header>
<h1>Our Services</h1>
<nav>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="about.html">About</a></li>
<li><a href="services.html">Services</a></li>
<li><a href="contact.html">Contact</a></li>
</ul>
</nav>
</header>
<main>
<section>
<h2>What We Offer</h2>
<p>This page details the services provided by our company.</p>
</section>
</main>
<footer>
<p>&copy; 2026 Our Company</p>
</footer>
<script src="../js/main.js"></script>
</body>
</html>
+32
View File
@@ -0,0 +1,32 @@
# retoor <retoor@molodetz.nl>
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[project]
name = "dwn-tests"
version = "1.0.0"
description = "Integration tests for DWN WebSocket API"
requires-python = ">=3.10"
dependencies = [
"pytest",
"pytest-asyncio",
"websockets",
]
[project.optional-dependencies]
dev = [
"pytest-cov",
]
[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
testpaths = ["."]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = "-v --tb=short"
filterwarnings = [
"ignore::DeprecationWarning",
]
+188
View File
@@ -0,0 +1,188 @@
# retoor <retoor@molodetz.nl>
import pytest
from helpers.async_client import DWNAsyncClient
from helpers.event_collector import EventCollector
class TestAIIsAvailable:
@pytest.mark.asyncio
async def test_ai_is_available_returns_ok(self, dwn_client: DWNAsyncClient) -> None:
result = await dwn_client.ai_is_available()
assert result.get("status") == "ok"
@pytest.mark.asyncio
async def test_ai_is_available_structure(self, dwn_client: DWNAsyncClient) -> None:
result = await dwn_client.ai_is_available()
assert result.get("status") == "ok"
assert "available" in result
assert isinstance(result["available"], bool)
@pytest.mark.asyncio
async def test_ai_is_available_has_model_when_available(self, dwn_client: DWNAsyncClient) -> None:
result = await dwn_client.ai_is_available()
assert result.get("status") == "ok"
if result.get("available"):
assert "model" in result
class TestAICommand:
@pytest.mark.asyncio
async def test_ai_command_when_unavailable(self, dwn_client: DWNAsyncClient) -> None:
available = await dwn_client.ai_is_available()
if not available.get("available"):
result = await dwn_client.ai_command("Hello")
assert result.get("status") == "error"
assert "available" in result.get("message", "").lower() or "not configured" in result.get("message", "").lower()
else:
pytest.skip("AI is available, cannot test unavailable scenario")
@pytest.mark.asyncio
async def test_ai_command_returns_response(self, dwn_client: DWNAsyncClient) -> None:
available = await dwn_client.ai_is_available()
if not available.get("available"):
pytest.skip("AI not available")
result = await dwn_client.ai_command("What is 2+2?")
assert result.get("status") in ["ok", "error"]
if result.get("status") == "ok":
assert "response" in result
@pytest.mark.asyncio
async def test_ai_command_async_mode(self, dwn_client: DWNAsyncClient) -> None:
available = await dwn_client.ai_is_available()
if not available.get("available"):
pytest.skip("AI not available")
result = await dwn_client.ai_command("Hello", async_mode=True)
assert result.get("status") in ["ok", "pending", "error"]
class TestExaIsAvailable:
@pytest.mark.asyncio
async def test_exa_is_available_returns_ok(self, dwn_client: DWNAsyncClient) -> None:
result = await dwn_client.exa_is_available()
assert result.get("status") == "ok"
@pytest.mark.asyncio
async def test_exa_is_available_structure(self, dwn_client: DWNAsyncClient) -> None:
result = await dwn_client.exa_is_available()
assert result.get("status") == "ok"
assert "available" in result
assert isinstance(result["available"], bool)
class TestExaSearch:
@pytest.mark.asyncio
async def test_exa_search_when_unavailable(self, dwn_client: DWNAsyncClient) -> None:
available = await dwn_client.exa_is_available()
if not available.get("available"):
result = await dwn_client.exa_search("test query")
assert result.get("status") == "error"
else:
pytest.skip("Exa is available, cannot test unavailable scenario")
@pytest.mark.asyncio
async def test_exa_search_returns_results(self, dwn_client: DWNAsyncClient) -> None:
available = await dwn_client.exa_is_available()
if not available.get("available"):
pytest.skip("Exa not available")
result = await dwn_client.exa_search("python programming")
assert result.get("status") in ["ok", "error"]
if result.get("status") == "ok":
assert "results" in result
assert isinstance(result["results"], list)
@pytest.mark.asyncio
async def test_exa_search_async_mode(self, dwn_client: DWNAsyncClient) -> None:
available = await dwn_client.exa_is_available()
if not available.get("available"):
pytest.skip("Exa not available")
result = await dwn_client.exa_search("test", async_mode=True)
assert result.get("status") in ["ok", "pending", "error"]
class TestAIAvailabilityConsistency:
@pytest.mark.asyncio
async def test_ai_availability_consistent(self, dwn_client: DWNAsyncClient) -> None:
result1 = await dwn_client.ai_is_available()
result2 = await dwn_client.ai_is_available()
assert result1.get("available") == result2.get("available")
@pytest.mark.asyncio
async def test_exa_availability_consistent(self, dwn_client: DWNAsyncClient) -> None:
result1 = await dwn_client.exa_is_available()
result2 = await dwn_client.exa_is_available()
assert result1.get("available") == result2.get("available")
class TestAIEventEmission:
@pytest.mark.asyncio
async def test_ai_command_emits_event_on_success(
self,
dwn_client: DWNAsyncClient,
event_collector: EventCollector
) -> None:
available = await dwn_client.ai_is_available()
if not available.get("available"):
pytest.skip("AI not available")
event_collector.clear()
result = await dwn_client.ai_command("Hello")
if result.get("status") == "ok":
await event_collector.collect_for(0.5)
events = event_collector.get_events("ai_response_received")
assert len(events) >= 0
@pytest.mark.asyncio
async def test_exa_search_emits_event_on_success(
self,
dwn_client: DWNAsyncClient,
event_collector: EventCollector
) -> None:
available = await dwn_client.exa_is_available()
if not available.get("available"):
pytest.skip("Exa not available")
event_collector.clear()
result = await dwn_client.exa_search("test")
if result.get("status") == "ok":
await event_collector.collect_for(0.5)
events = event_collector.get_events("exa_search_completed")
assert len(events) >= 0