codeplain 0.2.0__py3-none-any.whl → 0.2.1__py3-none-any.whl
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.
- {codeplain-0.2.0.dist-info → codeplain-0.2.1.dist-info}/METADATA +15 -16
- {codeplain-0.2.0.dist-info → codeplain-0.2.1.dist-info}/RECORD +15 -6
- {codeplain-0.2.0.dist-info → codeplain-0.2.1.dist-info}/WHEEL +1 -2
- docs/generate_cli.py +20 -0
- examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py +17 -0
- tests/__init__.py +1 -0
- tests/conftest.py +34 -0
- tests/test_git_utils.py +458 -0
- tests/test_imports.py +42 -0
- tests/test_plainfile.py +271 -0
- tests/test_plainfileparser.py +532 -0
- tests/test_plainspec.py +67 -0
- tests/test_requires.py +27 -0
- codeplain-0.2.0.dist-info/top_level.txt +0 -43
- {codeplain-0.2.0.dist-info → codeplain-0.2.1.dist-info}/entry_points.txt +0 -0
- {codeplain-0.2.0.dist-info → codeplain-0.2.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,32 +1,31 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeplain
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Transform plain language specifications into working code
|
|
5
|
+
License-File: LICENSE
|
|
5
6
|
Classifier: Environment :: Console
|
|
6
7
|
Classifier: Intended Audience :: Developers
|
|
7
8
|
Classifier: Operating System :: OS Independent
|
|
8
9
|
Classifier: Topic :: Software Development :: Code Generators
|
|
9
10
|
Requires-Python: >=3.11
|
|
10
|
-
|
|
11
|
-
License-File: LICENSE
|
|
12
|
-
Requires-Dist: python-liquid2==0.3.0
|
|
11
|
+
Requires-Dist: gitpython==3.1.42
|
|
13
12
|
Requires-Dist: mistletoe==1.3.0
|
|
13
|
+
Requires-Dist: networkx==3.6.1
|
|
14
|
+
Requires-Dist: python-frontmatter==1.1.0
|
|
15
|
+
Requires-Dist: python-liquid2==0.3.0
|
|
16
|
+
Requires-Dist: pyyaml==6.0.2
|
|
14
17
|
Requires-Dist: requests==2.32.3
|
|
18
|
+
Requires-Dist: rich==14.2.0
|
|
19
|
+
Requires-Dist: textual==1.0.0
|
|
15
20
|
Requires-Dist: tiktoken==0.12.0
|
|
16
|
-
Requires-Dist: PyYAML==6.0.2
|
|
17
|
-
Requires-Dist: gitpython==3.1.42
|
|
18
21
|
Requires-Dist: transitions==0.9.3
|
|
19
|
-
Requires-Dist: textual==1.0.0
|
|
20
|
-
Requires-Dist: rich==14.2.0
|
|
21
|
-
Requires-Dist: python-frontmatter==1.1.0
|
|
22
|
-
Requires-Dist: networkx==3.6.1
|
|
23
22
|
Provides-Extra: dev
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist: flake8==7.0.0; extra ==
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
Requires-Dist:
|
|
29
|
-
|
|
23
|
+
Requires-Dist: black==24.2.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: flake8==7.0.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: isort==5.13.2; extra == 'dev'
|
|
26
|
+
Requires-Dist: mypy==1.11.2; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest==8.3.5; extra == 'dev'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
30
29
|
|
|
31
30
|
# Codeplain plain2code renderer
|
|
32
31
|
|
|
@@ -22,9 +22,10 @@ plain_modules.py,sha256=iDqqamtix5KahMC_v-vfQ7yndugmqtBW1z6XxTB4x6w,4876
|
|
|
22
22
|
plain_spec.py,sha256=zC-VOb_UJOs8OxtEiwQJuonw7Lkmbi7YHyFvvCvUZNo,13529
|
|
23
23
|
spinner.py,sha256=Ro6Gd9Przf-whuHqPRY6HwI0T57yJjyNPbhDbigZKZE,2471
|
|
24
24
|
system_config.py,sha256=mgHLn-CRHLO9Y9vKyI_eFBreY_YhFad-ctZgBYp-rIg,1777
|
|
25
|
-
codeplain-0.2.0.dist-info/licenses/LICENSE,sha256=wsFi5dpbJurnRNfBj8q2RCcF3ryrmdRIfxc3lPcmc4c,1069
|
|
26
25
|
config/__init__.py,sha256=beYSsJWmBNHDP5rYmVDouqgEeP3t1lkkepbXJ-oq0F8,37
|
|
27
26
|
config/system_config.yaml,sha256=bB5Th5jxgXFyaIvceUPID1ReebMMXsyMibV4gtu9sWQ,1148
|
|
27
|
+
docs/generate_cli.py,sha256=xJmiihtdgqEDBYt_wBsRbniZvIfq7Q6tjAsi3SjoMJg,531
|
|
28
|
+
examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py,sha256=dwTowrHiVKKbrDv21v8xJC30Q57AXZkQasdGOO5JsBE,470
|
|
28
29
|
render_machine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
30
|
render_machine/code_renderer.py,sha256=jOoYivkZzCuGG947YlNPlvD1Dpd2xTWVK8pwfF_OqDU,2957
|
|
30
31
|
render_machine/conformance_test_helpers.py,sha256=6Ru7Dh24SLNIKWfz_sP5hE_EmWpvk6nfgN1T1yaCZus,2887
|
|
@@ -58,6 +59,14 @@ standard_template_library/golang-console-app-template.plain,sha256=M3gE1wZ6o_F-H
|
|
|
58
59
|
standard_template_library/python-console-app-template.plain,sha256=ofIJIkNXScGFTx2w8mY3lP4KmRml2GQv2EiEoXlbqqs,1005
|
|
59
60
|
standard_template_library/typescript-react-app-boilerplate.plain,sha256=6LFxhEOzWnDHdVpFj-XBTq6s4_2dy7LKV3lVkCzv8i4,114
|
|
60
61
|
standard_template_library/typescript-react-app-template.plain,sha256=SG1cBMVt1ncMRK4cOGOyVVj0MmsQTmF4MI67IBn1lFg,380
|
|
62
|
+
tests/__init__.py,sha256=Wk73Io62J15BtlLVIzxmASDWaaJkQLevS4BLK5LDAQg,16
|
|
63
|
+
tests/conftest.py,sha256=QZcp08htUlJGgmHDlRWFgsWXZ8o8IBWTD5QqJaUMlU8,790
|
|
64
|
+
tests/test_git_utils.py,sha256=Knc4R2ZkHYVj9iPFKhb0_ewaad_mxPrakOn6gpziHMc,13138
|
|
65
|
+
tests/test_imports.py,sha256=cdw7u93xNFD064sjwwEWy4oLHhJNlt1-DRDYhHjymFw,2078
|
|
66
|
+
tests/test_plainfile.py,sha256=N3f59hpxOO5zULdBtmXZM6KO2PKADoOL5d3WxNTUYXg,11106
|
|
67
|
+
tests/test_plainfileparser.py,sha256=9B9r2jEeUKDMhqb4TJEPK7aatYl1op1z92GDAD4fJ20,18685
|
|
68
|
+
tests/test_plainspec.py,sha256=ouS899oejStu8u1D3399y5NAxcKlNbLcinNoxZUp59M,2437
|
|
69
|
+
tests/test_requires.py,sha256=u8wY-UI7Ryat84htaI0yFXpl-PjX7G90EnMKM35S6No,1182
|
|
61
70
|
tui/__init__.py,sha256=_fy7FowY0RkWdn4kC7XV2jgvQPCj-2gE8fNUb-Mny0A,60
|
|
62
71
|
tui/components.py,sha256=l7f-jLk0sj9WM8z2IpaHksmSyytXe_bl2GBdHASoIg4,13901
|
|
63
72
|
tui/models.py,sha256=KMjlWubmPD41GA-BjgiqkaV9v7tLJ0fuWKBDPmZj4JA,1493
|
|
@@ -65,8 +74,8 @@ tui/plain2code_tui.py,sha256=7agVU0NnaCuF1LuOSEmjx-XfEOQ6HHr1HlWe_b1Tses,11695
|
|
|
65
74
|
tui/state_handlers.py,sha256=HbjgaV-9xGhp3E-3X114zOqPkeNcCjT-R1PbVRxVdso,12674
|
|
66
75
|
tui/styles.css,sha256=Umm2TLePmywizZGV4Nd8UezZRiK5pFyibYRbpRvGqbs,3056
|
|
67
76
|
tui/widget_helpers.py,sha256=VJorEM2PjRBzN-jIDmKJPolFgo2d8-2NmTumgC5xeNo,5229
|
|
68
|
-
codeplain-0.2.
|
|
69
|
-
codeplain-0.2.
|
|
70
|
-
codeplain-0.2.
|
|
71
|
-
codeplain-0.2.
|
|
72
|
-
codeplain-0.2.
|
|
77
|
+
codeplain-0.2.1.dist-info/METADATA,sha256=UmRjTWg_BVVg4NqJRvOi_Rb6ldLR4EwZDU4X0TpNI3w,4278
|
|
78
|
+
codeplain-0.2.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
79
|
+
codeplain-0.2.1.dist-info/entry_points.txt,sha256=oDZkBqu9WhtZApb_K6ia8-fn9aojwmAsgnKELceX5T4,46
|
|
80
|
+
codeplain-0.2.1.dist-info/licenses/LICENSE,sha256=wsFi5dpbJurnRNfBj8q2RCcF3ryrmdRIfxc3lPcmc4c,1069
|
|
81
|
+
codeplain-0.2.1.dist-info/RECORD,,
|
docs/generate_cli.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from plain2code_arguments import create_parser
|
|
5
|
+
|
|
6
|
+
# Add the parent directory to the path so we can import plain2code_arguments
|
|
7
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# Get the parser and generate help text
|
|
11
|
+
parser = create_parser()
|
|
12
|
+
help_text = parser.format_help()
|
|
13
|
+
|
|
14
|
+
# Create markdown
|
|
15
|
+
md = "# Plain2Code CLI Reference\n\n```text\n" + help_text + "\n```"
|
|
16
|
+
|
|
17
|
+
# Run generate_cli.py in the docs folder
|
|
18
|
+
|
|
19
|
+
with open("plain2code_cli.md", "w", encoding="utf-8") as f:
|
|
20
|
+
f.write(md)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import unittest
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestHelloWorld(unittest.TestCase):
|
|
8
|
+
def test_hello_world_output(self):
|
|
9
|
+
# Run the hello_world.py script and capture its output
|
|
10
|
+
result = subprocess.run(["python3", "hello_world.py"], capture_output=True, text=True)
|
|
11
|
+
|
|
12
|
+
# Check if the output matches the expected string
|
|
13
|
+
self.assertEqual(result.stdout.strip(), "hello, world")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if __name__ == "__main__":
|
|
17
|
+
unittest.main()
|
tests/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Tests package
|
tests/conftest.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
os.environ["MASTER_KEY"] = "test-master-key"
|
|
6
|
+
os.environ["GOOGLE_API_KEY"] = "test-google-api-key"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def test_data_path():
|
|
11
|
+
"""Returns the path to the test data directory."""
|
|
12
|
+
return os.path.dirname(__file__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture
|
|
16
|
+
def load_test_data(test_data_path):
|
|
17
|
+
"""Returns a function to load test data files."""
|
|
18
|
+
|
|
19
|
+
def _load(file_name):
|
|
20
|
+
file_path = os.path.join(test_data_path, file_name)
|
|
21
|
+
with open(file_path, "r") as file:
|
|
22
|
+
return file.read()
|
|
23
|
+
|
|
24
|
+
return _load
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture
|
|
28
|
+
def get_test_data_path(test_data_path):
|
|
29
|
+
"""Returns a function to get the path to a test data subfolder."""
|
|
30
|
+
|
|
31
|
+
def _get_path(subfolder_name):
|
|
32
|
+
return os.path.join(test_data_path, subfolder_name)
|
|
33
|
+
|
|
34
|
+
return _get_path
|
tests/test_git_utils.py
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tempfile
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from textwrap import dedent
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from git import Repo
|
|
8
|
+
|
|
9
|
+
from git_utils import (
|
|
10
|
+
BASE_FOLDER_COMMIT_MESSAGE,
|
|
11
|
+
FUNCTIONAL_REQUIREMENT_FINISHED_COMMIT_MESSAGE,
|
|
12
|
+
REFACTORED_CODE_COMMIT_MESSAGE,
|
|
13
|
+
add_all_files_and_commit,
|
|
14
|
+
diff,
|
|
15
|
+
init_git_repo,
|
|
16
|
+
revert_changes,
|
|
17
|
+
revert_to_commit_with_frid,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture
|
|
22
|
+
def temp_repo():
|
|
23
|
+
"""Create a temporary git repository for testing."""
|
|
24
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
25
|
+
init_git_repo(temp_dir)
|
|
26
|
+
|
|
27
|
+
# Create and commit initial file
|
|
28
|
+
file_path = Path(temp_dir) / "test.txt"
|
|
29
|
+
file_path.write_text("initial content\nline2\nline3\n")
|
|
30
|
+
|
|
31
|
+
repo = Repo(temp_dir)
|
|
32
|
+
repo.index.add(["test.txt"])
|
|
33
|
+
add_all_files_and_commit(temp_dir, FUNCTIONAL_REQUIREMENT_FINISHED_COMMIT_MESSAGE.format("1.1"))
|
|
34
|
+
|
|
35
|
+
yield temp_dir
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.fixture
|
|
39
|
+
def empty_repo():
|
|
40
|
+
"""Create an empty git repository for testing."""
|
|
41
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
42
|
+
init_git_repo(temp_dir)
|
|
43
|
+
yield temp_dir
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_empty_diff(temp_repo):
|
|
47
|
+
"""Test diff when there are no changes."""
|
|
48
|
+
result = diff(temp_repo, "1.1")
|
|
49
|
+
assert result == {}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_single_file_change(temp_repo):
|
|
53
|
+
"""Test diff with a single file change."""
|
|
54
|
+
repo = Repo(temp_repo)
|
|
55
|
+
|
|
56
|
+
# Modify the file
|
|
57
|
+
file_path = Path(temp_repo) / "test.txt"
|
|
58
|
+
file_path.write_text("modified content\nline2\nline3\n")
|
|
59
|
+
repo.index.add(["test.txt"])
|
|
60
|
+
repo.index.commit("Modified test.txt")
|
|
61
|
+
|
|
62
|
+
# Get diff
|
|
63
|
+
result = diff(temp_repo, "1.1")
|
|
64
|
+
|
|
65
|
+
assert "test.txt" in result
|
|
66
|
+
expected_diff = dedent(
|
|
67
|
+
"""
|
|
68
|
+
--- a/test.txt
|
|
69
|
+
+++ b/test.txt
|
|
70
|
+
@@ -1,3 +1,3 @@
|
|
71
|
+
-initial content
|
|
72
|
+
+modified content
|
|
73
|
+
line2
|
|
74
|
+
line3
|
|
75
|
+
"""
|
|
76
|
+
).strip()
|
|
77
|
+
assert result["test.txt"] == expected_diff
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_multiple_file_changes(temp_repo):
|
|
81
|
+
"""Test diff with multiple file changes."""
|
|
82
|
+
repo = Repo(temp_repo)
|
|
83
|
+
|
|
84
|
+
# Create and commit second file
|
|
85
|
+
file2_path = Path(temp_repo) / "file2.txt"
|
|
86
|
+
file2_path.write_text("file2 initial\nline2\n")
|
|
87
|
+
|
|
88
|
+
add_all_files_and_commit(temp_repo, "Added file2.txt", None, "1.2")
|
|
89
|
+
|
|
90
|
+
# Modify both files
|
|
91
|
+
file1_path = Path(temp_repo) / "test.txt"
|
|
92
|
+
file1_path.write_text("file1 modified\nline2\n")
|
|
93
|
+
|
|
94
|
+
file2_path.write_text("file2 modified\nline2")
|
|
95
|
+
|
|
96
|
+
# Get diff
|
|
97
|
+
result = diff(temp_repo, "1.1")
|
|
98
|
+
|
|
99
|
+
# Check first file
|
|
100
|
+
assert "test.txt" in result
|
|
101
|
+
expected_diff1 = dedent(
|
|
102
|
+
"""
|
|
103
|
+
--- a/test.txt
|
|
104
|
+
+++ b/test.txt
|
|
105
|
+
@@ -1,3 +1,2 @@
|
|
106
|
+
-initial content
|
|
107
|
+
+file1 modified
|
|
108
|
+
line2
|
|
109
|
+
-line3
|
|
110
|
+
"""
|
|
111
|
+
).strip()
|
|
112
|
+
assert result["test.txt"] == expected_diff1
|
|
113
|
+
|
|
114
|
+
# Check second file
|
|
115
|
+
assert "file2.txt" in result
|
|
116
|
+
expected_diff2 = dedent(
|
|
117
|
+
"""
|
|
118
|
+
--- /dev/null
|
|
119
|
+
+++ b/file2.txt
|
|
120
|
+
@@ -0,0 +1,2 @@
|
|
121
|
+
+file2 modified
|
|
122
|
+
+line2
|
|
123
|
+
\
|
|
124
|
+
"""
|
|
125
|
+
).strip()
|
|
126
|
+
assert result["file2.txt"] == expected_diff2
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_multiple_commits_diff(temp_repo):
|
|
130
|
+
"""Test diff with multiple commits."""
|
|
131
|
+
repo = Repo(temp_repo)
|
|
132
|
+
|
|
133
|
+
file1_path = Path(temp_repo) / "test.txt"
|
|
134
|
+
|
|
135
|
+
# Create and commit second file
|
|
136
|
+
file2_path = Path(temp_repo) / "file2.txt"
|
|
137
|
+
file2_path.write_text("file2 frid1.1 refactored version\nline2\n")
|
|
138
|
+
|
|
139
|
+
add_all_files_and_commit(temp_repo, REFACTORED_CODE_COMMIT_MESSAGE.format("1.1"), None, "1.1")
|
|
140
|
+
add_all_files_and_commit(temp_repo, FUNCTIONAL_REQUIREMENT_FINISHED_COMMIT_MESSAGE.format("1.1"), None, "1.1")
|
|
141
|
+
|
|
142
|
+
file1_path.write_text("file1 frid1.2 version\nline2\n")
|
|
143
|
+
file2_path.write_text("file2 frid1.2 version\nline2\n")
|
|
144
|
+
|
|
145
|
+
add_all_files_and_commit(temp_repo, "implemented frid 1.2", None, "1.2")
|
|
146
|
+
|
|
147
|
+
file1_path.write_text("file1 frid1.2 refactored version\nline2\n")
|
|
148
|
+
|
|
149
|
+
add_all_files_and_commit(temp_repo, REFACTORED_CODE_COMMIT_MESSAGE.format("1.2"), None, "1.2")
|
|
150
|
+
add_all_files_and_commit(temp_repo, FUNCTIONAL_REQUIREMENT_FINISHED_COMMIT_MESSAGE.format("1.2"), None, "1.2")
|
|
151
|
+
|
|
152
|
+
file3_path = Path(temp_repo) / "file3.txt"
|
|
153
|
+
file3_path.write_text("file3 frid1.2 new file\nline2\n")
|
|
154
|
+
|
|
155
|
+
# Get diff
|
|
156
|
+
result = diff(temp_repo, "1.1")
|
|
157
|
+
|
|
158
|
+
# Check first file
|
|
159
|
+
assert "test.txt" in result
|
|
160
|
+
expected_diff1 = dedent(
|
|
161
|
+
"""
|
|
162
|
+
--- a/test.txt
|
|
163
|
+
+++ b/test.txt
|
|
164
|
+
@@ -1,3 +1,2 @@
|
|
165
|
+
-initial content
|
|
166
|
+
+file1 frid1.2 refactored version
|
|
167
|
+
line2
|
|
168
|
+
-line3
|
|
169
|
+
"""
|
|
170
|
+
).strip()
|
|
171
|
+
assert result["test.txt"] == expected_diff1
|
|
172
|
+
|
|
173
|
+
# Check second file
|
|
174
|
+
assert "file2.txt" in result
|
|
175
|
+
expected_diff2 = dedent(
|
|
176
|
+
"""
|
|
177
|
+
--- a/file2.txt
|
|
178
|
+
+++ b/file2.txt
|
|
179
|
+
@@ -1,2 +1,2 @@
|
|
180
|
+
-file2 frid1.1 refactored version
|
|
181
|
+
+file2 frid1.2 version
|
|
182
|
+
line2
|
|
183
|
+
"""
|
|
184
|
+
).strip()
|
|
185
|
+
assert result["file2.txt"] == expected_diff2
|
|
186
|
+
|
|
187
|
+
# Check third file
|
|
188
|
+
assert "file3.txt" in result
|
|
189
|
+
expected_diff3 = dedent(
|
|
190
|
+
"""
|
|
191
|
+
--- /dev/null
|
|
192
|
+
+++ b/file3.txt
|
|
193
|
+
@@ -0,0 +1,2 @@
|
|
194
|
+
+file3 frid1.2 new file
|
|
195
|
+
+line2
|
|
196
|
+
"""
|
|
197
|
+
).strip()
|
|
198
|
+
assert result["file3.txt"] == expected_diff3
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def test_diff_without_previous_frid_and_no_base_folder(empty_repo):
|
|
202
|
+
"""Test diff without previous frid and no base folder."""
|
|
203
|
+
# Create a new file without committing
|
|
204
|
+
file_path = Path(empty_repo) / "new.txt"
|
|
205
|
+
file_path.write_text("new file content\nline2\n")
|
|
206
|
+
add_all_files_and_commit(empty_repo, "First commit")
|
|
207
|
+
|
|
208
|
+
# create one more file
|
|
209
|
+
file_path = Path(empty_repo) / "new2.txt"
|
|
210
|
+
file_path.write_text("new file content\nline2\n")
|
|
211
|
+
|
|
212
|
+
# Get diff
|
|
213
|
+
result = diff(empty_repo)
|
|
214
|
+
|
|
215
|
+
assert "new.txt" in result
|
|
216
|
+
expected_diff = dedent(
|
|
217
|
+
"""
|
|
218
|
+
--- /dev/null
|
|
219
|
+
+++ b/new.txt
|
|
220
|
+
@@ -0,0 +1,2 @@
|
|
221
|
+
+new file content
|
|
222
|
+
+line2
|
|
223
|
+
"""
|
|
224
|
+
).strip()
|
|
225
|
+
assert result["new.txt"] == expected_diff
|
|
226
|
+
|
|
227
|
+
assert "new2.txt" in result
|
|
228
|
+
expected_diff2 = dedent(
|
|
229
|
+
"""
|
|
230
|
+
--- /dev/null
|
|
231
|
+
+++ b/new2.txt
|
|
232
|
+
@@ -0,0 +1,2 @@
|
|
233
|
+
+new file content
|
|
234
|
+
+line2
|
|
235
|
+
"""
|
|
236
|
+
).strip()
|
|
237
|
+
assert result["new2.txt"] == expected_diff2
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def test_diff_without_previous_frid_and_base_folder(temp_repo):
|
|
241
|
+
"""Test diff without previous frid and base folder."""
|
|
242
|
+
# Create a commit for the base folder
|
|
243
|
+
file_path = Path(temp_repo) / "new.txt"
|
|
244
|
+
file_path.write_text("base folder content\nline2\n")
|
|
245
|
+
add_all_files_and_commit(temp_repo, BASE_FOLDER_COMMIT_MESSAGE)
|
|
246
|
+
|
|
247
|
+
# update the file
|
|
248
|
+
file_path.write_text("updated base folder content\nline2\n")
|
|
249
|
+
|
|
250
|
+
# Get diff
|
|
251
|
+
result = diff(temp_repo)
|
|
252
|
+
|
|
253
|
+
assert "new.txt" in result
|
|
254
|
+
expected_diff = dedent(
|
|
255
|
+
"""
|
|
256
|
+
--- a/new.txt
|
|
257
|
+
+++ b/new.txt
|
|
258
|
+
@@ -1,2 +1,2 @@
|
|
259
|
+
-base folder content
|
|
260
|
+
+updated base folder content
|
|
261
|
+
line2
|
|
262
|
+
"""
|
|
263
|
+
).strip()
|
|
264
|
+
assert result["new.txt"] == expected_diff
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def test_new_file(temp_repo):
|
|
268
|
+
"""Test diff with a new untracked file."""
|
|
269
|
+
repo = Repo(temp_repo)
|
|
270
|
+
|
|
271
|
+
# Create a new file without committing
|
|
272
|
+
file_path = Path(temp_repo) / "new.txt"
|
|
273
|
+
file_path.write_text("new file content\nline2\n")
|
|
274
|
+
|
|
275
|
+
# Get diff
|
|
276
|
+
result = diff(temp_repo, "1.1")
|
|
277
|
+
|
|
278
|
+
assert "new.txt" in result
|
|
279
|
+
expected_diff = dedent(
|
|
280
|
+
"""
|
|
281
|
+
--- /dev/null
|
|
282
|
+
+++ b/new.txt
|
|
283
|
+
@@ -0,0 +1,2 @@
|
|
284
|
+
+new file content
|
|
285
|
+
+line2
|
|
286
|
+
"""
|
|
287
|
+
).strip()
|
|
288
|
+
assert result["new.txt"] == expected_diff
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def test_deleted_file(temp_repo):
|
|
292
|
+
"""Test diff with a deleted file."""
|
|
293
|
+
repo = Repo(temp_repo)
|
|
294
|
+
|
|
295
|
+
# Delete the file
|
|
296
|
+
file_path = Path(temp_repo) / "test.txt"
|
|
297
|
+
file_path.unlink()
|
|
298
|
+
|
|
299
|
+
# Get diff
|
|
300
|
+
result = diff(temp_repo, "1.1")
|
|
301
|
+
|
|
302
|
+
assert "test.txt" in result
|
|
303
|
+
expected_diff = dedent(
|
|
304
|
+
"""
|
|
305
|
+
--- a/test.txt
|
|
306
|
+
+++ /dev/null
|
|
307
|
+
@@ -1,3 +0,0 @@
|
|
308
|
+
-initial content
|
|
309
|
+
-line2
|
|
310
|
+
-line3
|
|
311
|
+
"""
|
|
312
|
+
).strip()
|
|
313
|
+
assert result["test.txt"] == expected_diff
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def test_init_clean_repo(empty_repo):
|
|
317
|
+
"""Test initializing a clean git repository."""
|
|
318
|
+
repo = Repo(empty_repo)
|
|
319
|
+
|
|
320
|
+
# Verify it's a git repository
|
|
321
|
+
assert repo.git_dir is not None
|
|
322
|
+
assert repo.git_dir.endswith(".git")
|
|
323
|
+
|
|
324
|
+
# Verify there is only one commit
|
|
325
|
+
assert len(list(repo.iter_commits())) == 1
|
|
326
|
+
assert "[Codeplain] Initial module commit" in list(repo.iter_commits())[0].message
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def test_add_all_files_and_commit(temp_repo):
|
|
330
|
+
"""Test adding all files and committing them."""
|
|
331
|
+
# Create some test files
|
|
332
|
+
file1_path = Path(temp_repo) / "file1.txt"
|
|
333
|
+
file1_path.write_text("content1")
|
|
334
|
+
file2_path = Path(temp_repo) / "file2.txt"
|
|
335
|
+
file2_path.write_text("content2")
|
|
336
|
+
|
|
337
|
+
# Add and commit files
|
|
338
|
+
repo = add_all_files_and_commit(temp_repo, "Test commit", None, "FR123", "render-id")
|
|
339
|
+
|
|
340
|
+
# Verify commit was created
|
|
341
|
+
commits = list(repo.iter_commits())
|
|
342
|
+
assert len(commits) == 3 # initial commit + first FRID commit + test commit
|
|
343
|
+
|
|
344
|
+
# Verify commit message
|
|
345
|
+
commit = commits[0]
|
|
346
|
+
assert "Test commit" in commit.message
|
|
347
|
+
assert "Changes related to Functional requirement ID (FRID): FR123" in commit.message
|
|
348
|
+
assert "Render ID: render-id" in commit.message
|
|
349
|
+
|
|
350
|
+
# Verify files were committed
|
|
351
|
+
tree = commit.tree
|
|
352
|
+
assert "file1.txt" in tree
|
|
353
|
+
assert "file2.txt" in tree
|
|
354
|
+
|
|
355
|
+
file2_path.write_text("content2 modified")
|
|
356
|
+
repo = add_all_files_and_commit(temp_repo, "Commit changes on existing file", None, "FR4")
|
|
357
|
+
commits = list(repo.iter_commits())
|
|
358
|
+
assert len(commits) == 4
|
|
359
|
+
assert "Changes related to Functional requirement ID (FRID): FR4" in commits[0].message
|
|
360
|
+
|
|
361
|
+
repo = add_all_files_and_commit(temp_repo, "Empty commit", None, "FR5")
|
|
362
|
+
commits = list(repo.iter_commits())
|
|
363
|
+
assert len(commits) == 5
|
|
364
|
+
assert "Changes related to Functional requirement ID (FRID): FR5" in commits[0].message
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def test_revert_changes(temp_repo):
|
|
368
|
+
"""Test reverting changes in the repository."""
|
|
369
|
+
# Create and commit initial file
|
|
370
|
+
file_path = Path(temp_repo) / "test.txt"
|
|
371
|
+
file_path.write_text("initial content")
|
|
372
|
+
repo = add_all_files_and_commit(temp_repo, "Initial commit", None, "FR123")
|
|
373
|
+
|
|
374
|
+
# Modify the file
|
|
375
|
+
file_path.write_text("modified content")
|
|
376
|
+
|
|
377
|
+
# Verify the file was modified
|
|
378
|
+
assert file_path.read_text() == "modified content"
|
|
379
|
+
|
|
380
|
+
# Revert changes
|
|
381
|
+
repo = revert_changes(temp_repo)
|
|
382
|
+
|
|
383
|
+
# Verify the file was reverted
|
|
384
|
+
assert file_path.read_text() == "initial content"
|
|
385
|
+
|
|
386
|
+
# Verify working directory is clean
|
|
387
|
+
assert not repo.is_dirty()
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def test_revert_to_commit_with_frid(temp_repo):
|
|
391
|
+
"""Test reverting to a specific commit with FRID."""
|
|
392
|
+
# Create and commit first version
|
|
393
|
+
file_path = Path(temp_repo) / "test.txt"
|
|
394
|
+
file_path.write_text("version 1")
|
|
395
|
+
repo = add_all_files_and_commit(
|
|
396
|
+
temp_repo, FUNCTIONAL_REQUIREMENT_FINISHED_COMMIT_MESSAGE.format("FR123"), None, "FR123"
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
# Create and commit second version
|
|
400
|
+
file_path.write_text("version 2")
|
|
401
|
+
repo = add_all_files_and_commit(
|
|
402
|
+
temp_repo, FUNCTIONAL_REQUIREMENT_FINISHED_COMMIT_MESSAGE.format("FR456"), None, "FR456"
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
# Create and commit third version
|
|
406
|
+
file_path.write_text("version 3")
|
|
407
|
+
repo = add_all_files_and_commit(
|
|
408
|
+
temp_repo, FUNCTIONAL_REQUIREMENT_FINISHED_COMMIT_MESSAGE.format("FR789"), None, "FR789"
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
# Revert to FR123
|
|
412
|
+
repo = revert_to_commit_with_frid(temp_repo, "FR123")
|
|
413
|
+
|
|
414
|
+
# Verify we're back at version 1
|
|
415
|
+
assert file_path.read_text() == "version 1"
|
|
416
|
+
|
|
417
|
+
# Verify working directory is clean
|
|
418
|
+
assert not repo.is_dirty()
|
|
419
|
+
|
|
420
|
+
# Test error case - non-existent FRID
|
|
421
|
+
with pytest.raises(Exception) as exc_info:
|
|
422
|
+
revert_to_commit_with_frid(temp_repo, "NONEXISTENT")
|
|
423
|
+
assert "No commit with frid NONEXISTENT found" in str(exc_info.value)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def test_revert_to_commit_with_frid_and_base_folder(temp_repo):
|
|
427
|
+
"""Test reverting to base folder."""
|
|
428
|
+
# Create a commit for the base folder
|
|
429
|
+
file_path = Path(temp_repo) / "new.txt"
|
|
430
|
+
file_path.write_text("base folder content\nline1\n")
|
|
431
|
+
add_all_files_and_commit(temp_repo, BASE_FOLDER_COMMIT_MESSAGE)
|
|
432
|
+
|
|
433
|
+
# create another commit
|
|
434
|
+
file_path.write_text("changed file content\nline2\n")
|
|
435
|
+
add_all_files_and_commit(temp_repo, "Another commit")
|
|
436
|
+
|
|
437
|
+
# revert to base folder
|
|
438
|
+
repo = revert_to_commit_with_frid(temp_repo, None)
|
|
439
|
+
|
|
440
|
+
# verify the file was reverted
|
|
441
|
+
assert file_path.read_text() == "base folder content\nline1\n"
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def test_revert_to_base_folder_no_commit(temp_repo):
|
|
445
|
+
"""Test reverting to base folder."""
|
|
446
|
+
# Create a commit for the base folder
|
|
447
|
+
file_path = Path(temp_repo) / "new.txt"
|
|
448
|
+
file_path.write_text("some content\n")
|
|
449
|
+
add_all_files_and_commit(temp_repo, "FRID", 123)
|
|
450
|
+
|
|
451
|
+
# revert initial commit
|
|
452
|
+
repo = revert_to_commit_with_frid(temp_repo, None)
|
|
453
|
+
|
|
454
|
+
# verify the repo doesn't have any commits and that the file doesn't exist
|
|
455
|
+
assert not repo.is_dirty()
|
|
456
|
+
assert repo.active_branch.name == "main"
|
|
457
|
+
assert len(list(repo.iter_commits())) == 1 # initial commit
|
|
458
|
+
assert not file_path.exists()
|
tests/test_imports.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
import plain_file
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_non_existent_import(load_test_data, get_test_data_path):
|
|
7
|
+
plain_source_text = load_test_data("data/imports/non_existent_import.plain")
|
|
8
|
+
with pytest.raises(Exception, match="Module does not exist"):
|
|
9
|
+
plain_file.parse_plain_source(plain_source_text, {}, [], [], [])
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_circular_imports(load_test_data, get_test_data_path):
|
|
13
|
+
plain_source_text = load_test_data("data/imports/circular_imports_main.plain")
|
|
14
|
+
template_dirs = [get_test_data_path("data/imports")]
|
|
15
|
+
with pytest.raises(Exception, match="Circular import detected"):
|
|
16
|
+
plain_file.parse_plain_source(plain_source_text, {}, template_dirs, [], [])
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_diamond_imports(load_test_data, get_test_data_path):
|
|
20
|
+
plain_source_text = load_test_data("data/imports/diamond_imports_main.plain")
|
|
21
|
+
template_dirs = [get_test_data_path("data/imports")]
|
|
22
|
+
plain_file_parse_result = plain_file.parse_plain_source(plain_source_text, {}, template_dirs, [], [])
|
|
23
|
+
|
|
24
|
+
assert plain_file.marshall_plain_source(plain_file_parse_result.plain_source) == {
|
|
25
|
+
"definitions": [
|
|
26
|
+
{"markdown": "- :CommonImportDef: is a definition in diamond_import_common."},
|
|
27
|
+
{"markdown": "- :Import1Def: is a definition in diamond_import_1."},
|
|
28
|
+
{"markdown": "- :Import2Def: is a definition in diamond_import_2."},
|
|
29
|
+
],
|
|
30
|
+
"technical specs": [
|
|
31
|
+
{"markdown": "- :CommonImportDef: is used in diamond_import_common."},
|
|
32
|
+
{"markdown": "- :Import1Def: is used in diamond_import_1."},
|
|
33
|
+
{"markdown": "- :Import2Def: is used in diamond_import_2."},
|
|
34
|
+
{"markdown": '- :MainExecutableFile: of :App: should be called "hello_world.py".'},
|
|
35
|
+
],
|
|
36
|
+
"test specs": [
|
|
37
|
+
{"markdown": "- :CommonImportDef: is tested in diamond_import_common."},
|
|
38
|
+
{"markdown": "- :Import1Def: is tested in diamond_import_1."},
|
|
39
|
+
{"markdown": "- :Import2Def: is tested in diamond_import_2."},
|
|
40
|
+
],
|
|
41
|
+
"functional specs": [{"markdown": '- Display "hello, world"'}],
|
|
42
|
+
}
|