build: add -ldl linker flag, recursive source discovery, and API test targets to Makefile
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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>© 2026 Secure Web Inc.</p>
|
||||
</footer>
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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>© 2026 Secure Web Inc.</p>
|
||||
</footer>
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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>© 2026 Secure Web Inc.</p>
|
||||
</footer>
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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.');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
Extracted email addresses: test@example.com
|
||||
Identified words: hellohello, world, line1
|
||||
Original input: hellohello worldtest@example.comline1.
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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' });
|
||||
});
|
||||
});
|
||||
@@ -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>© 2026 Our Company</p>
|
||||
</footer>
|
||||
<script src="../js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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>© 2026 Our Company</p>
|
||||
</footer>
|
||||
<script src="../js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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>© 2026 Our Company</p>
|
||||
</footer>
|
||||
<script src="../js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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>© 2026 Our Company</p>
|
||||
</footer>
|
||||
<script src="../js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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",
|
||||
]
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user