Skip to content

Commit 5876e16

Browse files
authored
Merge branch 'main' into score-2774-traceability
2 parents 6287c69 + 1939bde commit 5876e16

8 files changed

Lines changed: 671 additions & 3614 deletions

File tree

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
- name: Run test targets
3737
run: |
3838
bazel run --lockfile_mode=error //:ide_support
39-
bazel test --lockfile_mode=error //src/... //score_pytest/...
39+
bazel test --lockfile_mode=error //...
4040
4141
- name: Prepare bundled consumer report
4242
if: always()

.pre-commit-config.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ repos:
2727
- id: check-added-large-files
2828
args: ["--maxkb=150"]
2929

30+
- repo: local
31+
hooks:
32+
- id: bazel-mod-tidy
33+
name: format Bazel module files
34+
entry: bazel mod tidy
35+
language: system
36+
pass_filenames: false
37+
files: '(^|/)(MODULE\.bazel|.*\.bzl|BUILD(\.bazel)?)$'
38+
3039
- repo: https://github.com/rhysd/actionlint
3140
rev: v1.7.11
3241
hooks:

MODULE.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,4 @@ http_file(
6969
urls = ["https://github.com/withered-magic/starpls/releases/download/v0.1.21/starpls-linux-amd64"],
7070
)
7171

72-
bazel_dep(name = "score_process", version = "1.5.3")
72+
bazel_dep(name = "score_process", version = "1.5.4")

MODULE.bazel.lock

Lines changed: 256 additions & 3554 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/how-to/other_modules.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ A minimal example (add or extend the existing `bazel_deps` stanza):
3535

3636
.. code-block:: starlark
3737
38-
bazel_dep(name = "score_process", version = "1.3.0")
38+
bazel_dep(name = "score_process", version = "1.5.3")
3939
4040
2) Extend your `docs` rule so Sphinx picks up the other module's inventory
4141
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

docs/how-to/setup.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ designed to enhance documentation capabilities in S-CORE.
2323
Add the module to your `MODULE.bazel` file:
2424

2525
```starlark
26-
bazel_dep(name = "score_docs_as_code", version = "2.0.3")
26+
bazel_dep(name = "score_docs_as_code", version = "4.0.1")
2727
```
2828

2929
And make sure to also add the S-core Bazel registry to your `.bazelrc` file

scripts_bazel/tests/generate_sourcelinks_cli_test.py

Lines changed: 152 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,63 @@
1111
# SPDX-License-Identifier: Apache-2.0
1212
# *******************************************************************************
1313

14+
# ╓ ╖
15+
# ║ Some parts are generated by Gemini ║
16+
# ╙ ╜
17+
1418
"""Tests for generate_sourcelinks_cli.py"""
1519

1620
import json
17-
import subprocess
1821
import sys
1922
from pathlib import Path
2023

24+
import pytest
25+
26+
import scripts_bazel.generate_sourcelinks_cli
27+
from src.extensions.score_source_code_linker.needlinks import is_metadata
28+
2129
_MY_PATH = Path(__file__).parent
2230

2331

24-
def test_generate_sourcelinks_cli_basic(tmp_path: Path) -> None:
32+
def assert_json_internal_types(input: list[dict[str, str | int]]):
33+
for entry in input:
34+
assert "file" in entry
35+
assert "line" in entry
36+
assert "tag" in entry
37+
assert "need" in entry
38+
assert "full_line" in entry
39+
40+
assert isinstance(entry["file"], str)
41+
assert isinstance(entry["line"], int)
42+
assert isinstance(entry["tag"], str)
43+
assert isinstance(entry["need"], str)
44+
assert isinstance(entry["full_line"], str)
45+
46+
47+
# Unit test generated by Gemini.
48+
@pytest.mark.parametrize(
49+
"input_path, expected_output",
50+
[
51+
# Case 1: Path starts with "external/" and has a project name
52+
(Path("external/score_docs_as_code+/docs/index.md"), Path("docs/index.md")),
53+
# Case 2: Path does NOT start with "external/"
54+
(Path("src/main.py"), Path("src/main.py")),
55+
# Case 3: Path has "external" elsewhere in the string (should not be removed)
56+
(Path("my_external_data/file.txt"), Path("my_external_data/file.txt")),
57+
# Case 4: Deeply nested path inside an external prefix
58+
(Path("external/repo/subfolder/file.py"), Path("subfolder/file.py")),
59+
# Case 5: Path is exactly "external/" (edge case, returns empty path based on split logic)
60+
(Path("external/"), Path("external")),
61+
],
62+
)
63+
def test_clean_external_prefix(input_path: Path, expected_output: Path):
64+
output = scripts_bazel.generate_sourcelinks_cli.clean_external_prefix(input_path)
65+
assert output == expected_output
66+
67+
68+
def test_generate_sourcelinks_cli_basic(
69+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
70+
) -> None:
2571
"""Test basic functionality of generate_sourcelinks_cli."""
2672
# Create a test source file with a traceability tag
2773
test_file = tmp_path / "test_source.py"
@@ -36,39 +82,116 @@ def some_function():
3682

3783
output_file = tmp_path / "output.json"
3884

39-
# Execute the script
40-
result = subprocess.run(
41-
[
42-
sys.executable,
43-
_MY_PATH.parent / "generate_sourcelinks_cli.py",
44-
"--output",
45-
str(output_file),
46-
str(test_file),
47-
],
48-
)
49-
50-
assert result.returncode == 0
51-
assert output_file.exists()
85+
test_args: list[Path | str] = [
86+
_MY_PATH.parent
87+
/ "generate_sourcelinks_cli.py", # sys.argv[0] is always the script name
88+
"--output",
89+
str(output_file),
90+
str(test_file),
91+
]
92+
monkeypatch.setattr(sys, "argv", test_args)
93+
result = scripts_bazel.generate_sourcelinks_cli.main()
94+
assert result == 0
5295

5396
# Check the output content
5497
with open(output_file) as f:
5598
data: list[dict[str, str | int]] = json.load(f)
5699
assert isinstance(data, list)
57-
assert len(data) > 0
100+
# The first dictionary has to be metadata
101+
assert len(data) == 2
102+
assert is_metadata(data[0])
103+
assert data[0]["repo_name"] == "local_repo"
104+
# hash & url can not be set in this script therefore HAVE to be empty
105+
assert data[0]["hash"] == ""
106+
assert data[0]["url"] == ""
58107

59108
# Verify schema of each entry
60-
for entry in data:
61-
assert "file" in entry
62-
assert "line" in entry
63-
assert "tag" in entry
64-
assert "need" in entry
65-
assert "full_line" in entry
109+
assert_json_internal_types(data[1:])
66110

67-
# Verify types
68-
assert isinstance(entry["file"], str)
69-
assert isinstance(entry["line"], int)
70-
assert isinstance(entry["tag"], str)
71-
assert isinstance(entry["need"], str)
72-
assert isinstance(entry["full_line"], str)
111+
assert data[1]["need"] == "tool_req__docs_arch_types"
73112

74-
assert any(entry["need"] == "tool_req__docs_arch_types" for entry in data)
113+
114+
def test_generate_sourcelinks_cli_parse_external_module(
115+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
116+
):
117+
# 1. Create the 'external' directory inside the temp path
118+
external_root = tmp_path / "external"
119+
external_root.mkdir()
120+
121+
# 2. Create your file inside that 'external' directory
122+
test_file = external_root / "score_baselibs+" / "src" / "source_file1.py"
123+
test_file.parent.mkdir(parents=True)
124+
test_file.write_text("content")
125+
126+
# 3. Create the path relative to tmp_path so it starts with 'external/'
127+
# Use .relative_to(tmp_path) to get 'external/score_docs_as_code+/...'
128+
test_file.write_text(
129+
"""
130+
# Some code here
131+
# req-Id: tool_req__docs_arch_types
132+
def some_function():
133+
pass
134+
"""
135+
)
136+
output_file = tmp_path / "output.json"
137+
monkeypatch.chdir(tmp_path)
138+
relative_test_file = test_file.relative_to(tmp_path)
139+
test_args: list[Path | str] = [
140+
_MY_PATH.parent
141+
/ "generate_sourcelinks_cli.py", # sys.argv[0] is always the script name
142+
"--output",
143+
str(output_file),
144+
str(relative_test_file),
145+
]
146+
monkeypatch.setattr(sys, "argv", test_args)
147+
result = scripts_bazel.generate_sourcelinks_cli.main()
148+
assert result == 0
149+
with open(output_file) as f:
150+
data: list[dict[str, str | int]] = json.load(f)
151+
assert isinstance(data, list)
152+
assert len(data) == 2
153+
# The first dictionary has to be metadata
154+
assert is_metadata(data[0])
155+
assert data[0]["repo_name"] == "score_baselibs"
156+
# hash & url can not be set in this script therefore HAVE to be empty
157+
assert data[0]["hash"] == ""
158+
assert data[0]["url"] == ""
159+
160+
# Verify schema of each entry
161+
assert_json_internal_types(data[1:])
162+
163+
164+
def test_generate_sourcelinks_cli_file_not_exists(
165+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
166+
):
167+
external_root = tmp_path / "external"
168+
external_root.mkdir()
169+
170+
# 2. Create your file inside that 'external' directory
171+
test_file = external_root / "score_baselibs+" / "src" / "source_file1.py"
172+
test_file.parent.mkdir(parents=True)
173+
test_file.write_text("content")
174+
175+
# 3. Create the path relative to tmp_path so it starts with 'external/'
176+
# Use .relative_to(tmp_path) to get 'external/score_docs_as_code+/...'
177+
test_file.write_text(
178+
"""
179+
# Some code here
180+
# req-Id: tool_req__docs_arch_types
181+
def some_function():
182+
pass
183+
"""
184+
)
185+
output_file = tmp_path / "output.json"
186+
# BY not changing directory (like above) we can FORCE the file to not exists
187+
relative_test_file = test_file.relative_to(tmp_path)
188+
test_args: list[Path | str] = [
189+
_MY_PATH.parent
190+
/ "generate_sourcelinks_cli.py", # sys.argv[0] is always the script name
191+
"--output",
192+
str(output_file),
193+
str(relative_test_file),
194+
]
195+
monkeypatch.setattr(sys, "argv", test_args)
196+
with pytest.raises(AssertionError):
197+
scripts_bazel.generate_sourcelinks_cli.main()

0 commit comments

Comments
 (0)