@theihtisham/dev-pulse 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +12 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +43 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +33 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +18 -0
- package/.github/dependabot.yml +16 -0
- package/.github/workflows/ci.yml +33 -0
- package/CODE_OF_CONDUCT.md +27 -0
- package/Dockerfile +8 -0
- package/LICENSE +21 -21
- package/README.md +135 -39
- package/SECURITY.md +22 -0
- package/devpulse/__init__.py +4 -4
- package/devpulse/api/__init__.py +1 -1
- package/devpulse/api/app.py +371 -371
- package/devpulse/cli/__init__.py +1 -1
- package/devpulse/cli/dashboard.py +131 -131
- package/devpulse/cli/main.py +678 -678
- package/devpulse/cli/render.py +175 -175
- package/devpulse/core/__init__.py +34 -34
- package/devpulse/core/analytics.py +487 -487
- package/devpulse/core/config.py +77 -77
- package/devpulse/core/database.py +612 -612
- package/devpulse/core/github_client.py +281 -281
- package/devpulse/core/models.py +142 -142
- package/devpulse/core/report_generator.py +454 -454
- package/devpulse/static/.gitkeep +1 -1
- package/devpulse/templates/report.html +64 -64
- package/package.json +35 -35
- package/pyproject.toml +80 -80
- package/requirements.txt +14 -14
- package/tests/__init__.py +1 -1
- package/tests/conftest.py +208 -208
- package/tests/test_analytics.py +284 -284
- package/tests/test_api.py +313 -313
- package/tests/test_cli.py +204 -204
- package/tests/test_config.py +47 -47
- package/tests/test_database.py +255 -255
- package/tests/test_models.py +107 -107
- package/tests/test_report_generator.py +173 -173
- package/jest.config.js +0 -7
package/tests/test_cli.py
CHANGED
|
@@ -1,204 +1,204 @@
|
|
|
1
|
-
"""Tests for CLI commands."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
from unittest.mock import patch, MagicMock
|
|
5
|
-
|
|
6
|
-
import pytest
|
|
7
|
-
from click.testing import CliRunner
|
|
8
|
-
|
|
9
|
-
from devpulse.cli.main import cli
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@pytest.fixture
|
|
13
|
-
def runner():
|
|
14
|
-
return CliRunner()
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class TestCLIBase:
|
|
18
|
-
"""Test CLI base commands."""
|
|
19
|
-
|
|
20
|
-
def test_version(self, runner):
|
|
21
|
-
result = runner.invoke(cli, ["--version"])
|
|
22
|
-
assert result.exit_code == 0
|
|
23
|
-
assert "1.0.0" in result.output
|
|
24
|
-
|
|
25
|
-
def test_help(self, runner):
|
|
26
|
-
result = runner.invoke(cli, ["--help"])
|
|
27
|
-
assert result.exit_code == 0
|
|
28
|
-
assert "DevPulse" in result.output
|
|
29
|
-
assert "sync" in result.output
|
|
30
|
-
assert "daily" in result.output
|
|
31
|
-
assert "metrics" in result.output
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class TestSyncCommand:
|
|
35
|
-
"""Test sync command (without actual GitHub)."""
|
|
36
|
-
|
|
37
|
-
def test_sync_no_token(self, runner, monkeypatch):
|
|
38
|
-
monkeypatch.delenv("DEVPULSE_GITHUB_TOKEN", raising=False)
|
|
39
|
-
monkeypatch.setenv("DEVPULSE_GITHUB_TOKEN", "")
|
|
40
|
-
from devpulse.core.config import reset_settings
|
|
41
|
-
reset_settings()
|
|
42
|
-
|
|
43
|
-
result = runner.invoke(cli, ["sync"])
|
|
44
|
-
assert result.exit_code != 0 or "DEVPULSE_GITHUB_TOKEN" in result.output
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class TestReportCommands:
|
|
48
|
-
"""Test report generation CLI commands."""
|
|
49
|
-
|
|
50
|
-
def test_daily_report_terminal(self, runner, populated_db):
|
|
51
|
-
with patch("devpulse.core.report_generator.ReportGenerator", return_value=MagicMock(
|
|
52
|
-
daily_report=MagicMock(return_value={
|
|
53
|
-
"date": "2026-04-01",
|
|
54
|
-
"author": "all",
|
|
55
|
-
"commits": 2,
|
|
56
|
-
"prs_opened": 0,
|
|
57
|
-
"prs_merged": 0,
|
|
58
|
-
"issues_opened": 0,
|
|
59
|
-
"issues_closed": 0,
|
|
60
|
-
"reviews_given": 0,
|
|
61
|
-
"lines_changed": 80,
|
|
62
|
-
"summary": "2 commit(s)",
|
|
63
|
-
"commit_details": [],
|
|
64
|
-
}),
|
|
65
|
-
)):
|
|
66
|
-
result = runner.invoke(cli, ["daily", "--date", "2026-04-01"])
|
|
67
|
-
# Should not crash (may print to console)
|
|
68
|
-
|
|
69
|
-
def test_daily_report_json(self, runner, populated_db):
|
|
70
|
-
with patch("devpulse.core.report_generator.ReportGenerator", return_value=MagicMock(
|
|
71
|
-
daily_report=MagicMock(return_value={
|
|
72
|
-
"date": "2026-04-01",
|
|
73
|
-
"author": "all",
|
|
74
|
-
"commits": 2,
|
|
75
|
-
"prs_opened": 0,
|
|
76
|
-
"prs_merged": 0,
|
|
77
|
-
"issues_opened": 0,
|
|
78
|
-
"issues_closed": 0,
|
|
79
|
-
"reviews_given": 0,
|
|
80
|
-
"lines_changed": 80,
|
|
81
|
-
"summary": "2 commits",
|
|
82
|
-
"commit_details": [],
|
|
83
|
-
}),
|
|
84
|
-
to_json=MagicMock(return_value='{"date": "2026-04-01"}'),
|
|
85
|
-
)):
|
|
86
|
-
result = runner.invoke(cli, ["daily", "--format", "json"])
|
|
87
|
-
# Should output JSON
|
|
88
|
-
|
|
89
|
-
def test_weekly_report_help(self, runner):
|
|
90
|
-
result = runner.invoke(cli, ["weekly", "--help"])
|
|
91
|
-
assert result.exit_code == 0
|
|
92
|
-
assert "--week-start" in result.output
|
|
93
|
-
|
|
94
|
-
def test_monthly_report_help(self, runner):
|
|
95
|
-
result = runner.invoke(cli, ["monthly", "--help"])
|
|
96
|
-
assert result.exit_code == 0
|
|
97
|
-
assert "--year" in result.output
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
class TestMetricsCommand:
|
|
101
|
-
"""Test metrics CLI commands."""
|
|
102
|
-
|
|
103
|
-
def test_metrics_help(self, runner):
|
|
104
|
-
result = runner.invoke(cli, ["metrics", "--help"])
|
|
105
|
-
assert result.exit_code == 0
|
|
106
|
-
assert "--author" in result.output
|
|
107
|
-
assert "--days" in result.output
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
class TestInsightsCommand:
|
|
111
|
-
"""Test insights CLI command."""
|
|
112
|
-
|
|
113
|
-
def test_insights_help(self, runner):
|
|
114
|
-
result = runner.invoke(cli, ["insights", "--help"])
|
|
115
|
-
assert result.exit_code == 0
|
|
116
|
-
assert "--days" in result.output
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
class TestHealthCommand:
|
|
120
|
-
"""Test health CLI command."""
|
|
121
|
-
|
|
122
|
-
def test_health_help(self, runner):
|
|
123
|
-
result = runner.invoke(cli, ["health", "--help"])
|
|
124
|
-
assert result.exit_code == 0
|
|
125
|
-
assert "--days" in result.output
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
class TestHeatmapCommand:
|
|
129
|
-
"""Test heatmap CLI command."""
|
|
130
|
-
|
|
131
|
-
def test_heatmap_help(self, runner):
|
|
132
|
-
result = runner.invoke(cli, ["heatmap", "--help"])
|
|
133
|
-
assert result.exit_code == 0
|
|
134
|
-
assert "--author" in result.output
|
|
135
|
-
assert "--days" in result.output
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
class TestGoalsCommands:
|
|
139
|
-
"""Test goals CLI commands."""
|
|
140
|
-
|
|
141
|
-
def test_goals_help(self, runner):
|
|
142
|
-
result = runner.invoke(cli, ["goals", "--help"])
|
|
143
|
-
assert result.exit_code == 0
|
|
144
|
-
|
|
145
|
-
def test_goals_add_help(self, runner):
|
|
146
|
-
result = runner.invoke(cli, ["goals", "add", "--help"])
|
|
147
|
-
assert result.exit_code == 0
|
|
148
|
-
assert "--title" in result.output
|
|
149
|
-
|
|
150
|
-
def test_goals_list_help(self, runner):
|
|
151
|
-
result = runner.invoke(cli, ["goals", "list", "--help"])
|
|
152
|
-
assert result.exit_code == 0
|
|
153
|
-
|
|
154
|
-
def test_goals_coach_help(self, runner):
|
|
155
|
-
result = runner.invoke(cli, ["goals", "coach", "--help"])
|
|
156
|
-
assert result.exit_code == 0
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
class TestSprintCommands:
|
|
160
|
-
"""Test sprint CLI commands."""
|
|
161
|
-
|
|
162
|
-
def test_sprint_help(self, runner):
|
|
163
|
-
result = runner.invoke(cli, ["sprint", "--help"])
|
|
164
|
-
assert result.exit_code == 0
|
|
165
|
-
|
|
166
|
-
def test_sprint_snapshot_help(self, runner):
|
|
167
|
-
result = runner.invoke(cli, ["sprint", "snapshot", "--help"])
|
|
168
|
-
assert result.exit_code == 0
|
|
169
|
-
assert "--name" in result.output
|
|
170
|
-
|
|
171
|
-
def test_sprint_burndown_help(self, runner):
|
|
172
|
-
result = runner.invoke(cli, ["sprint", "burndown", "--help"])
|
|
173
|
-
assert result.exit_code == 0
|
|
174
|
-
|
|
175
|
-
def test_sprint_predict_help(self, runner):
|
|
176
|
-
result = runner.invoke(cli, ["sprint", "predict", "--help"])
|
|
177
|
-
assert result.exit_code == 0
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
class TestQualityCommand:
|
|
181
|
-
"""Test quality CLI command."""
|
|
182
|
-
|
|
183
|
-
def test_quality_help(self, runner):
|
|
184
|
-
result = runner.invoke(cli, ["quality", "--help"])
|
|
185
|
-
assert result.exit_code == 0
|
|
186
|
-
assert "--repo" in result.output
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
class TestServeCommand:
|
|
190
|
-
"""Test serve CLI command."""
|
|
191
|
-
|
|
192
|
-
def test_serve_help(self, runner):
|
|
193
|
-
result = runner.invoke(cli, ["serve", "--help"])
|
|
194
|
-
assert result.exit_code == 0
|
|
195
|
-
assert "--port" in result.output
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
class TestDashboardCommand:
|
|
199
|
-
"""Test dashboard CLI command."""
|
|
200
|
-
|
|
201
|
-
def test_dashboard_help(self, runner):
|
|
202
|
-
result = runner.invoke(cli, ["dashboard", "--help"])
|
|
203
|
-
assert result.exit_code == 0
|
|
204
|
-
assert "--author" in result.output
|
|
1
|
+
"""Tests for CLI commands."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from unittest.mock import patch, MagicMock
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from click.testing import CliRunner
|
|
8
|
+
|
|
9
|
+
from devpulse.cli.main import cli
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.fixture
|
|
13
|
+
def runner():
|
|
14
|
+
return CliRunner()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestCLIBase:
|
|
18
|
+
"""Test CLI base commands."""
|
|
19
|
+
|
|
20
|
+
def test_version(self, runner):
|
|
21
|
+
result = runner.invoke(cli, ["--version"])
|
|
22
|
+
assert result.exit_code == 0
|
|
23
|
+
assert "1.0.0" in result.output
|
|
24
|
+
|
|
25
|
+
def test_help(self, runner):
|
|
26
|
+
result = runner.invoke(cli, ["--help"])
|
|
27
|
+
assert result.exit_code == 0
|
|
28
|
+
assert "DevPulse" in result.output
|
|
29
|
+
assert "sync" in result.output
|
|
30
|
+
assert "daily" in result.output
|
|
31
|
+
assert "metrics" in result.output
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class TestSyncCommand:
|
|
35
|
+
"""Test sync command (without actual GitHub)."""
|
|
36
|
+
|
|
37
|
+
def test_sync_no_token(self, runner, monkeypatch):
|
|
38
|
+
monkeypatch.delenv("DEVPULSE_GITHUB_TOKEN", raising=False)
|
|
39
|
+
monkeypatch.setenv("DEVPULSE_GITHUB_TOKEN", "")
|
|
40
|
+
from devpulse.core.config import reset_settings
|
|
41
|
+
reset_settings()
|
|
42
|
+
|
|
43
|
+
result = runner.invoke(cli, ["sync"])
|
|
44
|
+
assert result.exit_code != 0 or "DEVPULSE_GITHUB_TOKEN" in result.output
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TestReportCommands:
|
|
48
|
+
"""Test report generation CLI commands."""
|
|
49
|
+
|
|
50
|
+
def test_daily_report_terminal(self, runner, populated_db):
|
|
51
|
+
with patch("devpulse.core.report_generator.ReportGenerator", return_value=MagicMock(
|
|
52
|
+
daily_report=MagicMock(return_value={
|
|
53
|
+
"date": "2026-04-01",
|
|
54
|
+
"author": "all",
|
|
55
|
+
"commits": 2,
|
|
56
|
+
"prs_opened": 0,
|
|
57
|
+
"prs_merged": 0,
|
|
58
|
+
"issues_opened": 0,
|
|
59
|
+
"issues_closed": 0,
|
|
60
|
+
"reviews_given": 0,
|
|
61
|
+
"lines_changed": 80,
|
|
62
|
+
"summary": "2 commit(s)",
|
|
63
|
+
"commit_details": [],
|
|
64
|
+
}),
|
|
65
|
+
)):
|
|
66
|
+
result = runner.invoke(cli, ["daily", "--date", "2026-04-01"])
|
|
67
|
+
# Should not crash (may print to console)
|
|
68
|
+
|
|
69
|
+
def test_daily_report_json(self, runner, populated_db):
|
|
70
|
+
with patch("devpulse.core.report_generator.ReportGenerator", return_value=MagicMock(
|
|
71
|
+
daily_report=MagicMock(return_value={
|
|
72
|
+
"date": "2026-04-01",
|
|
73
|
+
"author": "all",
|
|
74
|
+
"commits": 2,
|
|
75
|
+
"prs_opened": 0,
|
|
76
|
+
"prs_merged": 0,
|
|
77
|
+
"issues_opened": 0,
|
|
78
|
+
"issues_closed": 0,
|
|
79
|
+
"reviews_given": 0,
|
|
80
|
+
"lines_changed": 80,
|
|
81
|
+
"summary": "2 commits",
|
|
82
|
+
"commit_details": [],
|
|
83
|
+
}),
|
|
84
|
+
to_json=MagicMock(return_value='{"date": "2026-04-01"}'),
|
|
85
|
+
)):
|
|
86
|
+
result = runner.invoke(cli, ["daily", "--format", "json"])
|
|
87
|
+
# Should output JSON
|
|
88
|
+
|
|
89
|
+
def test_weekly_report_help(self, runner):
|
|
90
|
+
result = runner.invoke(cli, ["weekly", "--help"])
|
|
91
|
+
assert result.exit_code == 0
|
|
92
|
+
assert "--week-start" in result.output
|
|
93
|
+
|
|
94
|
+
def test_monthly_report_help(self, runner):
|
|
95
|
+
result = runner.invoke(cli, ["monthly", "--help"])
|
|
96
|
+
assert result.exit_code == 0
|
|
97
|
+
assert "--year" in result.output
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class TestMetricsCommand:
|
|
101
|
+
"""Test metrics CLI commands."""
|
|
102
|
+
|
|
103
|
+
def test_metrics_help(self, runner):
|
|
104
|
+
result = runner.invoke(cli, ["metrics", "--help"])
|
|
105
|
+
assert result.exit_code == 0
|
|
106
|
+
assert "--author" in result.output
|
|
107
|
+
assert "--days" in result.output
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class TestInsightsCommand:
|
|
111
|
+
"""Test insights CLI command."""
|
|
112
|
+
|
|
113
|
+
def test_insights_help(self, runner):
|
|
114
|
+
result = runner.invoke(cli, ["insights", "--help"])
|
|
115
|
+
assert result.exit_code == 0
|
|
116
|
+
assert "--days" in result.output
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class TestHealthCommand:
|
|
120
|
+
"""Test health CLI command."""
|
|
121
|
+
|
|
122
|
+
def test_health_help(self, runner):
|
|
123
|
+
result = runner.invoke(cli, ["health", "--help"])
|
|
124
|
+
assert result.exit_code == 0
|
|
125
|
+
assert "--days" in result.output
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class TestHeatmapCommand:
|
|
129
|
+
"""Test heatmap CLI command."""
|
|
130
|
+
|
|
131
|
+
def test_heatmap_help(self, runner):
|
|
132
|
+
result = runner.invoke(cli, ["heatmap", "--help"])
|
|
133
|
+
assert result.exit_code == 0
|
|
134
|
+
assert "--author" in result.output
|
|
135
|
+
assert "--days" in result.output
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class TestGoalsCommands:
|
|
139
|
+
"""Test goals CLI commands."""
|
|
140
|
+
|
|
141
|
+
def test_goals_help(self, runner):
|
|
142
|
+
result = runner.invoke(cli, ["goals", "--help"])
|
|
143
|
+
assert result.exit_code == 0
|
|
144
|
+
|
|
145
|
+
def test_goals_add_help(self, runner):
|
|
146
|
+
result = runner.invoke(cli, ["goals", "add", "--help"])
|
|
147
|
+
assert result.exit_code == 0
|
|
148
|
+
assert "--title" in result.output
|
|
149
|
+
|
|
150
|
+
def test_goals_list_help(self, runner):
|
|
151
|
+
result = runner.invoke(cli, ["goals", "list", "--help"])
|
|
152
|
+
assert result.exit_code == 0
|
|
153
|
+
|
|
154
|
+
def test_goals_coach_help(self, runner):
|
|
155
|
+
result = runner.invoke(cli, ["goals", "coach", "--help"])
|
|
156
|
+
assert result.exit_code == 0
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class TestSprintCommands:
|
|
160
|
+
"""Test sprint CLI commands."""
|
|
161
|
+
|
|
162
|
+
def test_sprint_help(self, runner):
|
|
163
|
+
result = runner.invoke(cli, ["sprint", "--help"])
|
|
164
|
+
assert result.exit_code == 0
|
|
165
|
+
|
|
166
|
+
def test_sprint_snapshot_help(self, runner):
|
|
167
|
+
result = runner.invoke(cli, ["sprint", "snapshot", "--help"])
|
|
168
|
+
assert result.exit_code == 0
|
|
169
|
+
assert "--name" in result.output
|
|
170
|
+
|
|
171
|
+
def test_sprint_burndown_help(self, runner):
|
|
172
|
+
result = runner.invoke(cli, ["sprint", "burndown", "--help"])
|
|
173
|
+
assert result.exit_code == 0
|
|
174
|
+
|
|
175
|
+
def test_sprint_predict_help(self, runner):
|
|
176
|
+
result = runner.invoke(cli, ["sprint", "predict", "--help"])
|
|
177
|
+
assert result.exit_code == 0
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class TestQualityCommand:
|
|
181
|
+
"""Test quality CLI command."""
|
|
182
|
+
|
|
183
|
+
def test_quality_help(self, runner):
|
|
184
|
+
result = runner.invoke(cli, ["quality", "--help"])
|
|
185
|
+
assert result.exit_code == 0
|
|
186
|
+
assert "--repo" in result.output
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class TestServeCommand:
|
|
190
|
+
"""Test serve CLI command."""
|
|
191
|
+
|
|
192
|
+
def test_serve_help(self, runner):
|
|
193
|
+
result = runner.invoke(cli, ["serve", "--help"])
|
|
194
|
+
assert result.exit_code == 0
|
|
195
|
+
assert "--port" in result.output
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class TestDashboardCommand:
|
|
199
|
+
"""Test dashboard CLI command."""
|
|
200
|
+
|
|
201
|
+
def test_dashboard_help(self, runner):
|
|
202
|
+
result = runner.invoke(cli, ["dashboard", "--help"])
|
|
203
|
+
assert result.exit_code == 0
|
|
204
|
+
assert "--author" in result.output
|
package/tests/test_config.py
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
"""Tests for configuration management."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import pytest
|
|
5
|
-
|
|
6
|
-
from devpulse.core.config import Settings, get_settings, reset_settings
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class TestSettings:
|
|
10
|
-
"""Test settings loading."""
|
|
11
|
-
|
|
12
|
-
def test_default_settings(self):
|
|
13
|
-
reset_settings()
|
|
14
|
-
# Create Settings directly to check defaults (env vars are set by conftest)
|
|
15
|
-
s = Settings(github_token="", github_username="")
|
|
16
|
-
assert s.api_port == 8742
|
|
17
|
-
assert s.log_level == "INFO"
|
|
18
|
-
assert s.redact_sensitive is True
|
|
19
|
-
|
|
20
|
-
def test_env_override(self, monkeypatch):
|
|
21
|
-
reset_settings()
|
|
22
|
-
monkeypatch.setenv("DEVPULSE_GITHUB_TOKEN", "ghp_test123")
|
|
23
|
-
monkeypatch.setenv("DEVPULSE_API_PORT", "9999")
|
|
24
|
-
settings = Settings()
|
|
25
|
-
assert settings.github_token == "ghp_test123"
|
|
26
|
-
assert settings.api_port == 9999
|
|
27
|
-
|
|
28
|
-
def test_get_settings_caches(self):
|
|
29
|
-
reset_settings()
|
|
30
|
-
s1 = get_settings()
|
|
31
|
-
s2 = get_settings()
|
|
32
|
-
assert s1 is s2
|
|
33
|
-
|
|
34
|
-
def test_reset_settings(self):
|
|
35
|
-
reset_settings()
|
|
36
|
-
s1 = get_settings()
|
|
37
|
-
reset_settings()
|
|
38
|
-
s2 = get_settings()
|
|
39
|
-
assert s1 is not s2
|
|
40
|
-
|
|
41
|
-
def test_database_path_default(self):
|
|
42
|
-
settings = Settings()
|
|
43
|
-
assert "devpulse.db" in settings.database_path
|
|
44
|
-
|
|
45
|
-
def test_burnout_threshold(self):
|
|
46
|
-
settings = Settings()
|
|
47
|
-
assert 0 < settings.burnout_threshold < 1
|
|
1
|
+
"""Tests for configuration management."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from devpulse.core.config import Settings, get_settings, reset_settings
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestSettings:
|
|
10
|
+
"""Test settings loading."""
|
|
11
|
+
|
|
12
|
+
def test_default_settings(self):
|
|
13
|
+
reset_settings()
|
|
14
|
+
# Create Settings directly to check defaults (env vars are set by conftest)
|
|
15
|
+
s = Settings(github_token="", github_username="")
|
|
16
|
+
assert s.api_port == 8742
|
|
17
|
+
assert s.log_level == "INFO"
|
|
18
|
+
assert s.redact_sensitive is True
|
|
19
|
+
|
|
20
|
+
def test_env_override(self, monkeypatch):
|
|
21
|
+
reset_settings()
|
|
22
|
+
monkeypatch.setenv("DEVPULSE_GITHUB_TOKEN", "ghp_test123")
|
|
23
|
+
monkeypatch.setenv("DEVPULSE_API_PORT", "9999")
|
|
24
|
+
settings = Settings()
|
|
25
|
+
assert settings.github_token == "ghp_test123"
|
|
26
|
+
assert settings.api_port == 9999
|
|
27
|
+
|
|
28
|
+
def test_get_settings_caches(self):
|
|
29
|
+
reset_settings()
|
|
30
|
+
s1 = get_settings()
|
|
31
|
+
s2 = get_settings()
|
|
32
|
+
assert s1 is s2
|
|
33
|
+
|
|
34
|
+
def test_reset_settings(self):
|
|
35
|
+
reset_settings()
|
|
36
|
+
s1 = get_settings()
|
|
37
|
+
reset_settings()
|
|
38
|
+
s2 = get_settings()
|
|
39
|
+
assert s1 is not s2
|
|
40
|
+
|
|
41
|
+
def test_database_path_default(self):
|
|
42
|
+
settings = Settings()
|
|
43
|
+
assert "devpulse.db" in settings.database_path
|
|
44
|
+
|
|
45
|
+
def test_burnout_threshold(self):
|
|
46
|
+
settings = Settings()
|
|
47
|
+
assert 0 < settings.burnout_threshold < 1
|