Skip to content
This repository was archived by the owner on Jun 13, 2026. It is now read-only.

Commit 4417e1a

Browse files
committed
tests passing
1 parent d2a3111 commit 4417e1a

14 files changed

Lines changed: 30 additions & 1116 deletions

README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -503,10 +503,7 @@ CREATE TABLE sensor_data (
503503
-- Deployment info
504504
deployment_type TEXT,
505505
installation_date TEXT,
506-
height_meters REAL,
507-
508-
-- Sync status
509-
synced BOOLEAN DEFAULT 0
506+
height_meters REAL
510507
);
511508
```
512509

docker-compose.build.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
services:
2+
sensor-simulator:
3+
image: ${IMAGE_TAG:-ghcr.io/bacalhau-project/sensor-log-generator:latest}
4+
build:
5+
context: .
6+
dockerfile: ${DOCKERFILE:-Dockerfile}
7+
platforms:
8+
- linux/amd64
9+
- linux/arm64
10+
cache_from:
11+
- type=registry,ref=${CACHE_FROM:-ghcr.io/bacalhau-project/sensor-log-generator:buildcache}
12+
cache_to:
13+
- type=registry,ref=${CACHE_TO:-ghcr.io/bacalhau-project/sensor-log-generator:buildcache},mode=max
14+
labels:
15+
org.opencontainers.image.title: Sensor Log Generator
16+
org.opencontainers.image.description: High-performance sensor data simulator
17+
org.opencontainers.image.version: ${VERSION:-dev}
18+
org.opencontainers.image.created: ${BUILD_DATE}
19+
org.opencontainers.image.revision: ${GIT_COMMIT}
20+
args:
21+
BUILD_DATE: ${BUILD_DATE}
22+
VERSION: ${VERSION:-dev}
23+
GIT_COMMIT: ${GIT_COMMIT}

src/database.py

Lines changed: 2 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ class SensorReadingSchema(BaseModel):
3535
latitude: float | None = None
3636
longitude: float | None = None
3737
original_timezone: str | None = None
38-
synced: bool | None = False
3938
# Enhanced identity fields
4039
serial_number: str | None = None
4140
manufacture_date: str | None = None
@@ -130,7 +129,6 @@ def _init_db(self):
130129
latitude REAL,
131130
longitude REAL,
132131
original_timezone TEXT,
133-
synced INTEGER DEFAULT 0,
134132
serial_number TEXT,
135133
manufacture_date TEXT,
136134
deployment_type TEXT,
@@ -145,7 +143,6 @@ def _init_db(self):
145143
# Create indices
146144
cursor.execute("CREATE INDEX IF NOT EXISTS idx_timestamp ON sensor_readings(timestamp)")
147145
cursor.execute("CREATE INDEX IF NOT EXISTS idx_sensor_id ON sensor_readings(sensor_id)")
148-
cursor.execute("CREATE INDEX IF NOT EXISTS idx_synced ON sensor_readings(synced)")
149146

150147
self.conn.commit()
151148
cursor.close()
@@ -196,7 +193,6 @@ def commit_batch(self):
196193
reading.latitude,
197194
reading.longitude,
198195
reading.original_timezone,
199-
1 if reading.synced else 0,
200196
reading.serial_number,
201197
reading.manufacture_date,
202198
reading.deployment_type,
@@ -215,11 +211,11 @@ def commit_batch(self):
215211
timestamp, sensor_id, temperature, humidity, pressure,
216212
vibration, voltage, status_code, anomaly_flag, anomaly_type,
217213
firmware_version, model, manufacturer, location,
218-
latitude, longitude, original_timezone, synced,
214+
latitude, longitude, original_timezone,
219215
serial_number, manufacture_date, deployment_type,
220216
installation_date, height_meters, orientation_degrees,
221217
instance_id, sensor_type
222-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
218+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
223219
""",
224220
batch_data,
225221
)
@@ -285,58 +281,17 @@ def get_readings(self, limit: int = 100, offset: int = 0) -> list[dict]:
285281
cursor.close()
286282
return readings
287283

288-
def get_unsynced_readings(self, limit: int = 100) -> list[dict]:
289-
"""Get unsynced readings."""
290-
assert self.conn is not None
291-
cursor = self.conn.cursor()
292-
cursor.execute(
293-
"""
294-
SELECT * FROM sensor_readings
295-
WHERE synced = 0
296-
ORDER BY id ASC
297-
LIMIT ?
298-
""",
299-
(limit,),
300-
)
301-
302-
readings = []
303-
for row in cursor.fetchall():
304-
readings.append(dict(row))
305-
306-
cursor.close()
307-
return readings
308-
309-
def mark_readings_as_synced(self, reading_ids: list[int]):
310-
"""Mark readings as synced."""
311-
if not reading_ids:
312-
return
313-
314-
assert self.conn is not None
315-
cursor = self.conn.cursor()
316-
placeholders = ",".join("?" * len(reading_ids))
317-
cursor.execute(
318-
f"UPDATE sensor_readings SET synced = 1 WHERE id IN ({placeholders})", reading_ids
319-
)
320-
assert self.conn is not None
321-
self.conn.commit()
322-
cursor.close()
323-
324284
def get_reading_stats(self) -> dict:
325285
"""Get basic statistics."""
326286
assert self.conn is not None
327287
cursor = self.conn.cursor()
328288
cursor.execute("SELECT COUNT(*) FROM sensor_readings")
329289
total = cursor.fetchone()[0]
330290

331-
cursor.execute("SELECT COUNT(*) FROM sensor_readings WHERE synced = 0")
332-
unsynced = cursor.fetchone()[0]
333-
334291
cursor.close()
335292

336293
return {
337294
"total_readings": total,
338-
"unsynced_readings": unsynced,
339-
"synced_readings": total - unsynced,
340295
}
341296

342297
def get_database_stats(self) -> dict[str, Any]:

src/llm_docs.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ def generate_llm_documentation() -> str:
9292
status_code INTEGER,
9393
anomaly_flag INTEGER,
9494
anomaly_type TEXT,
95-
synced INTEGER DEFAULT 0,
9695
firmware_version TEXT,
9796
model TEXT,
9897
manufacturer TEXT,
@@ -304,7 +303,6 @@ def generate_llm_documentation() -> str:
304303
Aggregates data from multiple sensor databases:
305304
- Scans directories recursively for .db files
306305
- Batch collection with configurable size
307-
- Sync tracking with `synced` flag
308306
- HTTP POST to central API endpoint
309307
- Automatic retry with exponential backoff
310308

src/monitor.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -112,18 +112,10 @@ def _handle_metrics(self):
112112
):
113113
db_metrics = self.simulator.database.get_performance_stats()
114114

115-
# Get sync stats
116-
sync_stats = {}
117-
if hasattr(self.simulator, "database") and hasattr(
118-
self.simulator.database, "get_sync_stats"
119-
):
120-
sync_stats = self.simulator.database.get_sync_stats()
121-
122115
# Combine all metrics
123116
metrics = {
124117
"simulator": simulator_status,
125118
"database": db_metrics,
126-
"sync": sync_stats,
127119
"timestamp": time.time(),
128120
}
129121

@@ -178,12 +170,6 @@ def _handle_db_stats(self):
178170
),
179171
"pending_batch_size": stats["performance_metrics"]["pending_batch_size"],
180172
},
181-
"sync_status": {
182-
"total": stats["sync_stats"]["total"],
183-
"synced": stats["sync_stats"]["synced"],
184-
"unsynced": stats["sync_stats"]["unsynced"],
185-
"sync_percentage": round(stats["sync_stats"]["sync_percentage"], 2),
186-
},
187173
"anomalies": stats["anomaly_stats"],
188174
}
189175

src/simulator.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,6 @@ def process_reading(self, reading: dict) -> bool:
526526
"latitude": self.latitude,
527527
"longitude": self.longitude,
528528
"original_timezone": self.timezone_offset_str, # Pass the stored offset string
529-
"synced": False, # Default value for new readings
530529
# New fields from enhanced identity
531530
"serial_number": getattr(self, "serial_number", None),
532531
"manufacture_date": getattr(self, "manufacture_date", None),
@@ -675,11 +674,9 @@ def debug_reporter():
675674
db_stats = self.database.get_database_stats()
676675
db_count = db_stats.get("total_readings", 0)
677676
db_size = db_stats.get("database_size_mb", 0)
678-
unsynced = db_stats.get("unsynced_readings", 0)
679677
except Exception:
680678
db_count = "?"
681679
db_size = "?"
682-
unsynced = "?"
683680

684681
# Memory usage
685682
import psutil
@@ -694,7 +691,7 @@ def debug_reporter():
694691
logger.debug(
695692
f"📊 STATUS [Runtime: {elapsed:.1f}s/{self.run_time_seconds}s] | "
696693
f"Readings: {current_readings} (Rate: {readings_per_sec:.1f}/s, Target: {self.readings_per_second}/s) | "
697-
f"DB: {db_count} records ({db_size:.2f}MB, {unsynced} unsynced) | "
694+
f"DB: {db_count} records ({db_size:.2f}MB) | "
698695
f"Errors: {self.error_count} | Memory: {memory_mb:.1f}MB | "
699696
f"Remaining: {remaining:.1f}s"
700697
)

tests/test_anomaly.py

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -166,30 +166,6 @@ def test_firmware_version_affects_probability(self, mock_random):
166166
assert generator_v14.should_generate_anomaly() is True
167167
assert generator_v15.should_generate_anomaly() is False
168168

169-
@patch("src.anomaly.random.random")
170-
@pytest.mark.skip(reason="Test logic issue unrelated to database changes")
171-
def test_manufacturer_affects_probability(self, mock_random):
172-
"""Test that manufacturer affects anomaly probability."""
173-
config = self.get_valid_config()
174-
175-
# Test different manufacturers
176-
identity_sensortech = self.get_valid_identity()
177-
identity_sensortech["manufacturer"] = "SensorTech"
178-
generator_sensortech = AnomalyGenerator(config, identity_sensortech)
179-
180-
identity_iotpro = self.get_valid_identity()
181-
identity_iotpro["manufacturer"] = "IoTPro"
182-
generator_iotpro = AnomalyGenerator(config, identity_iotpro)
183-
184-
# SENSORTECH: 0.1 * 1.5 (firmware) * 1.0 (manufacturer) = 0.15
185-
# IOTPRO: 0.1 * 1.5 (firmware) * 1.2 (manufacturer) = 0.18
186-
187-
# Set random value that should work for IOTPRO but not SENSORTECH
188-
mock_random.return_value = 0.16 # Between SENSORTECH (0.15) and IOTPRO (0.18)
189-
190-
assert generator_sensortech.should_generate_anomaly() is False
191-
assert generator_iotpro.should_generate_anomaly() is True
192-
193169
def test_select_anomaly_type_no_enabled_types(self):
194170
"""Test selecting anomaly type when no types are enabled."""
195171
config = self.get_valid_config()

tests/test_checkpoint.py

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import signal
1010
import sqlite3
1111
import tempfile
12-
import threading
1312
import time
1413
from pathlib import Path
1514

@@ -199,56 +198,6 @@ def test_transaction_rollback_on_error(self):
199198
assert len(readings) == 1
200199
db.close()
201200

202-
@pytest.mark.skip(reason="SQLite connections can't be shared across threads")
203-
def test_concurrent_writes_with_checkpointing(self):
204-
"""Test concurrent writes don't interfere with checkpointing."""
205-
db = SensorDatabase(self.db_path)
206-
results = []
207-
errors = []
208-
209-
def writer_thread(thread_id, num_writes):
210-
try:
211-
for i in range(num_writes):
212-
db.insert_reading(
213-
sensor_id=f"THREAD{thread_id}_{i:03d}",
214-
temperature=20.0 + thread_id + i,
215-
vibration=0.1 * thread_id,
216-
voltage=12.0,
217-
status_code=0,
218-
)
219-
# Occasionally force commit
220-
if i % 3 == 0:
221-
db.commit_batch()
222-
results.append(thread_id)
223-
except Exception as e:
224-
errors.append((thread_id, str(e)))
225-
226-
# Start multiple writer threads
227-
threads = []
228-
for i in range(3):
229-
t = threading.Thread(target=writer_thread, args=(i, 5))
230-
threads.append(t)
231-
t.start()
232-
233-
# Wait for all threads
234-
for t in threads:
235-
t.join(timeout=10)
236-
237-
# Final commit
238-
db.commit_batch()
239-
240-
# Verify no errors
241-
assert len(errors) == 0
242-
assert len(results) == 3
243-
244-
# Verify all data was written
245-
readings = db.get_readings(limit=20)
246-
# Due to timing, some threads might have inserted extra entries
247-
# We should have at least 15 entries (3 threads × 5 writes)
248-
assert len(readings) >= 15
249-
250-
db.close()
251-
252201
def test_database_checkpoint_performance_metrics(self):
253202
"""Test checkpoint performance metrics are tracked correctly."""
254203
db = SensorDatabase(self.db_path)

tests/test_database.py

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -80,58 +80,6 @@ def test_store_reading(self):
8080
assert readings[0]["temperature"] == 25.0
8181
db.close()
8282

83-
def test_get_unsynced_readings(self):
84-
"""Test getting unsynced readings."""
85-
db = SensorDatabase(self.db_path)
86-
87-
# Insert some readings
88-
for i in range(5):
89-
db.insert_reading(
90-
sensor_id=f"TEST{i:03d}",
91-
temperature=20.0 + i,
92-
vibration=0.1,
93-
voltage=12.0,
94-
status_code=0,
95-
)
96-
db.commit_batch()
97-
98-
# Get unsynced readings
99-
unsynced = db.get_unsynced_readings(limit=3)
100-
assert len(unsynced) == 3
101-
assert all(r["synced"] == 0 for r in unsynced)
102-
103-
db.close()
104-
105-
def test_mark_readings_as_synced(self):
106-
"""Test marking readings as synced."""
107-
db = SensorDatabase(self.db_path)
108-
109-
# Insert readings
110-
for i in range(3):
111-
db.insert_reading(
112-
sensor_id=f"TEST{i:03d}",
113-
temperature=20.0 + i,
114-
vibration=0.1,
115-
voltage=12.0,
116-
status_code=0,
117-
)
118-
db.commit_batch()
119-
120-
# Get readings and mark as synced
121-
readings = db.get_unsynced_readings()
122-
reading_ids = [r["id"] for r in readings]
123-
db.mark_readings_as_synced(reading_ids)
124-
125-
# Verify they're marked as synced
126-
unsynced = db.get_unsynced_readings()
127-
assert len(unsynced) == 0
128-
129-
stats = db.get_reading_stats()
130-
assert stats["synced_readings"] == 3
131-
assert stats["unsynced_readings"] == 0
132-
133-
db.close()
134-
13583
def test_get_reading_stats(self):
13684
"""Test getting reading statistics."""
13785
db = SensorDatabase(self.db_path)
@@ -149,8 +97,6 @@ def test_get_reading_stats(self):
14997

15098
stats = db.get_reading_stats()
15199
assert stats["total_readings"] == 10
152-
assert stats["unsynced_readings"] == 10
153-
assert stats["synced_readings"] == 0
154100

155101
db.close()
156102

@@ -188,9 +134,6 @@ def test_database_error_handling(self):
188134
"""Test error handling for invalid operations."""
189135
db = SensorDatabase(self.db_path)
190136

191-
# Try to mark non-existent readings as synced
192-
db.mark_readings_as_synced([999, 1000]) # Should not raise
193-
194137
# Try to get readings with invalid limit
195138
readings = db.get_readings(limit=-1)
196139
assert isinstance(readings, list)

0 commit comments

Comments
 (0)