lsst-felis 29.2025.1700__tar.gz → 29.2025.1900__tar.gz

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.

Potentially problematic release.


This version of lsst-felis might be problematic. Click here for more details.

Files changed (40) hide show
  1. {lsst_felis-29.2025.1700/python/lsst_felis.egg-info → lsst_felis-29.2025.1900}/PKG-INFO +1 -1
  2. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/cli.py +6 -1
  3. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/datamodel.py +20 -0
  4. lsst_felis-29.2025.1900/python/felis/tests/run_cli.py +79 -0
  5. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900/python/lsst_felis.egg-info}/PKG-INFO +1 -1
  6. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/lsst_felis.egg-info/SOURCES.txt +1 -0
  7. lsst_felis-29.2025.1900/tests/test_cli.py +184 -0
  8. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/tests/test_datamodel.py +6 -0
  9. lsst_felis-29.2025.1700/tests/test_cli.py +0 -263
  10. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/COPYRIGHT +0 -0
  11. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/LICENSE +0 -0
  12. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/README.rst +0 -0
  13. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/pyproject.toml +0 -0
  14. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/__init__.py +0 -0
  15. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/db/__init__.py +0 -0
  16. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/db/dialects.py +0 -0
  17. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/db/schema.py +0 -0
  18. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/db/sqltypes.py +0 -0
  19. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/db/utils.py +0 -0
  20. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/db/variants.py +0 -0
  21. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/diff.py +0 -0
  22. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/metadata.py +0 -0
  23. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/py.typed +0 -0
  24. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/schemas/tap_schema_std.yaml +0 -0
  25. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/tap_schema.py +0 -0
  26. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/tests/__init__.py +0 -0
  27. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/tests/postgresql.py +0 -0
  28. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/felis/types.py +0 -0
  29. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/lsst_felis.egg-info/dependency_links.txt +0 -0
  30. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/lsst_felis.egg-info/entry_points.txt +0 -0
  31. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/lsst_felis.egg-info/requires.txt +0 -0
  32. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/lsst_felis.egg-info/top_level.txt +0 -0
  33. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/python/lsst_felis.egg-info/zip-safe +0 -0
  34. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/setup.cfg +0 -0
  35. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/tests/test_db.py +0 -0
  36. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/tests/test_diff.py +0 -0
  37. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/tests/test_metadata.py +0 -0
  38. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/tests/test_postgres.py +0 -0
  39. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/tests/test_tap_schema.py +0 -0
  40. {lsst_felis-29.2025.1700 → lsst_felis-29.2025.1900}/tests/test_tap_schema_postgres.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lsst-felis
3
- Version: 29.2025.1700
3
+ Version: 29.2025.1900
4
4
  Summary: A vocabulary for describing catalogs and acting on those descriptions
5
5
  Author-email: Rubin Observatory Data Management <dm-admin@lists.lsst.org>
6
6
  License: GNU General Public License v3 or later (GPLv3+)
@@ -63,7 +63,10 @@ loglevel_choices = ["CRITICAL", "FATAL", "ERROR", "WARNING", "INFO", "DEBUG"]
63
63
  help="Felis log file path",
64
64
  )
65
65
  @click.option(
66
- "--id-generation", is_flag=True, help="Generate IDs for all objects that do not have them", default=False
66
+ "--id-generation/--no-id-generation",
67
+ is_flag=True,
68
+ help="Generate IDs for all objects that do not have them",
69
+ default=True,
67
70
  )
68
71
  @click.pass_context
69
72
  def cli(ctx: click.Context, log_level: str, log_file: str | None, id_generation: bool) -> None:
@@ -72,6 +75,8 @@ def cli(ctx: click.Context, log_level: str, log_file: str | None, id_generation:
72
75
  ctx.obj["id_generation"] = id_generation
73
76
  if ctx.obj["id_generation"]:
74
77
  logger.info("ID generation is enabled")
78
+ else:
79
+ logger.info("ID generation is disabled")
75
80
  if log_file:
76
81
  logging.basicConfig(filename=log_file, level=log_level)
77
82
  else:
@@ -504,6 +504,26 @@ class Column(BaseObject):
504
504
  """
505
505
  return DataType(value)
506
506
 
507
+ @model_validator(mode="after")
508
+ def check_votable_xtype(self) -> Column:
509
+ """Set the default value for the ``votable_xtype`` field, which
510
+ corresponds to an Extended Datatype or ``xtype`` in the IVOA VOTable
511
+ standard.
512
+
513
+ Returns
514
+ -------
515
+ `Column`
516
+ The column being validated.
517
+
518
+ Notes
519
+ -----
520
+ This is currently only set automatically for the Felis ``timestamp``
521
+ datatype.
522
+ """
523
+ if self.datatype == DataType.timestamp and self.votable_xtype is None:
524
+ self.votable_xtype = "timestamp"
525
+ return self
526
+
507
527
 
508
528
  class Constraint(BaseObject):
509
529
  """Table constraint model."""
@@ -0,0 +1,79 @@
1
+ """Test utility for running cli commands."""
2
+
3
+ # This file is part of felis.
4
+ #
5
+ # Developed for the LSST Data Management System.
6
+ # This product includes software developed by the LSST Project
7
+ # (https://www.lsst.org).
8
+ # See the COPYRIGHT file at the top-level directory of this distribution
9
+ # for details of code ownership.
10
+ #
11
+ # This program is free software: you can redistribute it and/or modify
12
+ # it under the terms of the GNU General Public License as published by
13
+ # the Free Software Foundation, either version 3 of the License, or
14
+ # (at your option) any later version.
15
+ #
16
+ # This program is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ # GNU General Public License for more details.
20
+ #
21
+ # You should have received a copy of the GNU General Public License
22
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
23
+
24
+ import logging
25
+
26
+ from click.testing import CliRunner
27
+
28
+ from felis.cli import cli
29
+
30
+ __all__ = ["run_cli"]
31
+
32
+
33
+ def run_cli(
34
+ cmd: list[str],
35
+ log_level: int = logging.WARNING,
36
+ catch_exceptions: bool = False,
37
+ expect_error: bool = False,
38
+ print_cmd: bool = False,
39
+ print_output: bool = False,
40
+ id_generation: bool = False,
41
+ ) -> None:
42
+ """Run a CLI command and check the exit code.
43
+
44
+ Parameters
45
+ ----------
46
+ cmd : list[str]
47
+ The command to run.
48
+ log_level : int
49
+ The logging level to use, by default logging.WARNING.
50
+ catch_exceptions : bool
51
+ Whether to catch exceptions, by default False.
52
+ expect_error : bool
53
+ Whether to expect an error, by default False.
54
+ print_cmd : bool
55
+ Whether to print the command, by default False.
56
+ print_output : bool
57
+ Whether to print the output, by default False.
58
+ id_generation : bool
59
+ Whether to enable id generation, by default False.
60
+ """
61
+ if not cmd:
62
+ raise ValueError("No command provided.")
63
+ full_cmd = ["--log-level", logging.getLevelName(log_level)] + cmd
64
+ if id_generation:
65
+ full_cmd = ["--id-generation"] + full_cmd
66
+ if print_cmd:
67
+ print(f"Running command: felis {' '.join(full_cmd)}")
68
+ runner = CliRunner()
69
+ result = runner.invoke(
70
+ cli,
71
+ full_cmd,
72
+ catch_exceptions=catch_exceptions,
73
+ )
74
+ if print_output:
75
+ print(result.output)
76
+ if expect_error:
77
+ assert result.exit_code != 0
78
+ else:
79
+ assert result.exit_code == 0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lsst-felis
3
- Version: 29.2025.1700
3
+ Version: 29.2025.1900
4
4
  Summary: A vocabulary for describing catalogs and acting on those descriptions
5
5
  Author-email: Rubin Observatory Data Management <dm-admin@lists.lsst.org>
6
6
  License: GNU General Public License v3 or later (GPLv3+)
@@ -20,6 +20,7 @@ python/felis/db/variants.py
20
20
  python/felis/schemas/tap_schema_std.yaml
21
21
  python/felis/tests/__init__.py
22
22
  python/felis/tests/postgresql.py
23
+ python/felis/tests/run_cli.py
23
24
  python/lsst_felis.egg-info/PKG-INFO
24
25
  python/lsst_felis.egg-info/SOURCES.txt
25
26
  python/lsst_felis.egg-info/dependency_links.txt
@@ -0,0 +1,184 @@
1
+ # This file is part of felis.
2
+ #
3
+ # Developed for the LSST Data Management System.
4
+ # This product includes software developed by the LSST Project
5
+ # (https://www.lsst.org).
6
+ # See the COPYRIGHT file at the top-level directory of this distribution
7
+ # for details of code ownership.
8
+ #
9
+ # This program is free software: you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # This program is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
21
+
22
+ import os
23
+ import shutil
24
+ import tempfile
25
+ import unittest
26
+
27
+ from sqlalchemy import create_engine
28
+
29
+ import felis.tap_schema as tap_schema
30
+ from felis.datamodel import Schema
31
+ from felis.metadata import MetaDataBuilder
32
+ from felis.tests.run_cli import run_cli
33
+
34
+ TEST_DIR = os.path.abspath(os.path.dirname(__file__))
35
+ TEST_YAML = os.path.join(TEST_DIR, "data", "test.yml")
36
+
37
+
38
+ class CliTestCase(unittest.TestCase):
39
+ """Tests for CLI commands."""
40
+
41
+ def setUp(self) -> None:
42
+ """Set up a temporary directory for tests."""
43
+ self.tmpdir = tempfile.mkdtemp(dir=TEST_DIR)
44
+ self.sqlite_url = f"sqlite:///{self.tmpdir}/db.sqlite3"
45
+ print(f"Using temporary directory: {self.tmpdir}")
46
+
47
+ def tearDown(self) -> None:
48
+ """Clean up temporary directory."""
49
+ shutil.rmtree(self.tmpdir, ignore_errors=True)
50
+
51
+ def test_invalid_command(self) -> None:
52
+ """Test for invalid command."""
53
+ run_cli(["invalid"], expect_error=True)
54
+
55
+ def test_help(self) -> None:
56
+ """Test for help command."""
57
+ run_cli(["--help"], print_output=True)
58
+
59
+ def test_create(self) -> None:
60
+ """Test for create command."""
61
+ run_cli(["create", f"--engine-url={self.sqlite_url}", TEST_YAML])
62
+
63
+ def test_create_with_dry_run(self) -> None:
64
+ """Test for ``create --dry-run`` command."""
65
+ run_cli(["create", "--schema-name=main", f"--engine-url={self.sqlite_url}", "--dry-run", TEST_YAML])
66
+
67
+ def test_create_with_ignore_constraints(self) -> None:
68
+ """Test ``--ignore-constraints`` flag of ``create`` command."""
69
+ run_cli(
70
+ [
71
+ "create",
72
+ "--schema-name=main",
73
+ "--ignore-constraints",
74
+ f"--engine-url={self.sqlite_url}",
75
+ "--dry-run",
76
+ TEST_YAML,
77
+ ]
78
+ )
79
+
80
+ def test_validate(self) -> None:
81
+ """Test validate command."""
82
+ run_cli(["validate", TEST_YAML])
83
+
84
+ def test_validate_with_id_generation(self) -> None:
85
+ """Test that loading a schema with IDs works if ID generation is
86
+ enabled. This is the default behavior.
87
+ """
88
+ test_yaml = os.path.join(TEST_DIR, "data", "test_id_generation.yaml")
89
+ run_cli(["--id-generation", "validate", test_yaml])
90
+
91
+ def test_validate_with_id_generation_error(self) -> None:
92
+ """Test that loading a schema without IDs fails if ID generation is not
93
+ enabled.
94
+ """
95
+ test_yaml = os.path.join(TEST_DIR, "data", "test_id_generation.yaml")
96
+ run_cli(["--no-id-generation", "validate", test_yaml], expect_error=True)
97
+
98
+ def test_validate_with_extra_checks(self) -> None:
99
+ """Test schema validation flags."""
100
+ run_cli(
101
+ [
102
+ "validate",
103
+ "--check-description",
104
+ "--check-tap-principal",
105
+ "--check-tap-table-indexes",
106
+ TEST_YAML,
107
+ ]
108
+ )
109
+
110
+ def test_create_with_initialize_and_drop_error(self) -> None:
111
+ """Test that initialize and drop can't be used together."""
112
+ run_cli(["create", "--initialize", "--drop", TEST_YAML], expect_error=True)
113
+
114
+ def test_load_tap_schema(self) -> None:
115
+ """Test load-tap-schema command."""
116
+ # Create the TAP_SCHEMA database.
117
+ tap_schema_path = tap_schema.TableManager.get_tap_schema_std_path()
118
+ run_cli(["--id-generation", "create", f"--engine-url={self.sqlite_url}", tap_schema_path])
119
+
120
+ # Load the TAP_SCHEMA data.
121
+ run_cli(["load-tap-schema", f"--engine-url={self.sqlite_url}", TEST_YAML])
122
+
123
+ def test_init_tap_schema(self) -> None:
124
+ """Test init-tap-schema command."""
125
+ run_cli(["init-tap-schema", f"--engine-url={self.sqlite_url}"])
126
+
127
+ def test_init_tap_schema_mock(self) -> None:
128
+ """Test init-tap-schema command with a mock URL, which should throw
129
+ an error, as this is not supported.
130
+ """
131
+ run_cli(["init-tap-schema", "sqlite://"], expect_error=True)
132
+
133
+ def test_diff(self) -> None:
134
+ """Test for ``diff`` command."""
135
+ test_diff1 = os.path.join(TEST_DIR, "data", "test_diff1.yaml")
136
+ test_diff2 = os.path.join(TEST_DIR, "data", "test_diff2.yaml")
137
+
138
+ run_cli(["diff", test_diff1, test_diff2])
139
+
140
+ def test_diff_database(self) -> None:
141
+ """Test for ``diff`` command with database."""
142
+ test_diff1 = os.path.join(TEST_DIR, "data", "test_diff1.yaml")
143
+ test_diff2 = os.path.join(TEST_DIR, "data", "test_diff2.yaml")
144
+
145
+ engine = create_engine(self.sqlite_url)
146
+ metadata_db = MetaDataBuilder(Schema.from_uri(test_diff1), apply_schema_to_metadata=False).build()
147
+ metadata_db.create_all(engine)
148
+
149
+ run_cli(["diff", f"--engine-url={self.sqlite_url}", test_diff2])
150
+
151
+ def test_diff_alembic(self) -> None:
152
+ """Test for ``diff`` command with ``--alembic`` comparator option."""
153
+ test_diff1 = os.path.join(TEST_DIR, "data", "test_diff1.yaml")
154
+ test_diff2 = os.path.join(TEST_DIR, "data", "test_diff2.yaml")
155
+ run_cli(["diff", "--comparator", "alembic", test_diff1, test_diff2], print_output=True)
156
+
157
+ def test_diff_error(self) -> None:
158
+ """Test for ``diff`` command with bad arguments."""
159
+ test_diff1 = os.path.join(TEST_DIR, "data", "test_diff1.yaml")
160
+ run_cli(["diff", test_diff1], expect_error=True)
161
+
162
+ def test_diff_error_on_change(self) -> None:
163
+ """Test for ``diff`` command with ``--error-on-change`` flag."""
164
+ test_diff1 = os.path.join(TEST_DIR, "data", "test_diff1.yaml")
165
+ test_diff2 = os.path.join(TEST_DIR, "data", "test_diff2.yaml")
166
+ run_cli(["diff", "--error-on-change", test_diff1, test_diff2], expect_error=True, print_output=True)
167
+
168
+ def test_dump_yaml(self) -> None:
169
+ """Test for ``dump`` command with YAML output."""
170
+ with tempfile.NamedTemporaryFile(delete=True, suffix=".yaml") as temp_file:
171
+ run_cli(["dump", TEST_YAML, temp_file.name], print_output=True)
172
+
173
+ def test_dump_json(self) -> None:
174
+ """Test for ``dump`` command with JSON output."""
175
+ with tempfile.NamedTemporaryFile(delete=True, suffix=".json") as temp_file:
176
+ run_cli(["dump", TEST_YAML, temp_file.name], print_output=True)
177
+
178
+ def test_dump_with_invalid_file_extension_error(self) -> None:
179
+ """Test for ``dump`` command with JSON output."""
180
+ run_cli(["dump", TEST_YAML, "out.bad"], expect_error=True)
181
+
182
+
183
+ if __name__ == "__main__":
184
+ unittest.main()
@@ -231,6 +231,12 @@ class ColumnTestCase(unittest.TestCase):
231
231
  bool_coldata["value"] = True
232
232
  Column(**bool_coldata)
233
233
 
234
+ def test_timestamp(self) -> None:
235
+ """Test validation of timestamp columns."""
236
+ # Check that the votable_xtype is set correctly for timestamp columns.
237
+ col = Column(name="testColumn", id="#test_col_id", datatype="timestamp")
238
+ self.assertEqual(col.votable_xtype, "timestamp")
239
+
234
240
 
235
241
  class TableTestCase(unittest.TestCase):
236
242
  """Test Pydantic validation of the ``Table`` class."""
@@ -1,263 +0,0 @@
1
- # This file is part of felis.
2
- #
3
- # Developed for the LSST Data Management System.
4
- # This product includes software developed by the LSST Project
5
- # (https://www.lsst.org).
6
- # See the COPYRIGHT file at the top-level directory of this distribution
7
- # for details of code ownership.
8
- #
9
- # This program is free software: you can redistribute it and/or modify
10
- # it under the terms of the GNU General Public License as published by
11
- # the Free Software Foundation, either version 3 of the License, or
12
- # (at your option) any later version.
13
- #
14
- # This program is distributed in the hope that it will be useful,
15
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
- # GNU General Public License for more details.
18
- #
19
- # You should have received a copy of the GNU General Public License
20
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
21
-
22
- import os
23
- import shutil
24
- import tempfile
25
- import unittest
26
-
27
- from click.testing import CliRunner
28
- from sqlalchemy import create_engine
29
-
30
- import felis.tap_schema as tap_schema
31
- from felis.cli import cli
32
- from felis.datamodel import Schema
33
- from felis.metadata import MetaDataBuilder
34
-
35
- TESTDIR = os.path.abspath(os.path.dirname(__file__))
36
- TEST_YAML = os.path.join(TESTDIR, "data", "test.yml")
37
-
38
-
39
- class CliTestCase(unittest.TestCase):
40
- """Tests for CLI commands."""
41
-
42
- def setUp(self) -> None:
43
- """Set up a temporary directory for tests."""
44
- self.tmpdir = tempfile.mkdtemp(dir=TESTDIR)
45
-
46
- def tearDown(self) -> None:
47
- """Clean up temporary directory."""
48
- shutil.rmtree(self.tmpdir, ignore_errors=True)
49
-
50
- def test_create_all(self) -> None:
51
- """Test for create command."""
52
- url = f"sqlite:///{self.tmpdir}/tap.sqlite3"
53
-
54
- runner = CliRunner()
55
- result = runner.invoke(
56
- cli,
57
- ["create", "--schema-name=main", f"--engine-url={url}", TEST_YAML],
58
- catch_exceptions=False,
59
- )
60
- self.assertEqual(result.exit_code, 0)
61
-
62
- def test_create_all_dry_run(self) -> None:
63
- """Test for ``create --dry-run`` command."""
64
- url = f"sqlite:///{self.tmpdir}/tap.sqlite3"
65
-
66
- runner = CliRunner()
67
- result = runner.invoke(
68
- cli,
69
- ["create", "--schema-name=main", f"--engine-url={url}", "--dry-run", TEST_YAML],
70
- catch_exceptions=False,
71
- )
72
- self.assertEqual(result.exit_code, 0)
73
-
74
- def test_ignore_constraints(self) -> None:
75
- """Test ``--ignore-constraints`` flag of ``create`` command."""
76
- url = f"sqlite:///{self.tmpdir}/tap.sqlite3"
77
-
78
- runner = CliRunner()
79
- result = runner.invoke(
80
- cli,
81
- [
82
- "create",
83
- "--schema-name=main",
84
- "--ignore-constraints",
85
- f"--engine-url={url}",
86
- "--dry-run",
87
- TEST_YAML,
88
- ],
89
- catch_exceptions=False,
90
- )
91
- self.assertEqual(result.exit_code, 0)
92
-
93
- def test_validate_default(self) -> None:
94
- """Test validate command."""
95
- runner = CliRunner()
96
- result = runner.invoke(cli, ["validate", TEST_YAML], catch_exceptions=False)
97
- self.assertEqual(result.exit_code, 0)
98
-
99
- def test_id_generation(self) -> None:
100
- """Test the ``--id-generation`` flag."""
101
- test_yaml = os.path.join(TESTDIR, "data", "test_id_generation.yaml")
102
- runner = CliRunner()
103
- result = runner.invoke(cli, ["--id-generation", "validate", test_yaml], catch_exceptions=False)
104
- self.assertEqual(result.exit_code, 0)
105
-
106
- def test_no_id_generation(self) -> None:
107
- """Test that loading a schema without IDs fails if ID generation is not
108
- enabled.
109
- """
110
- test_yaml = os.path.join(TESTDIR, "data", "test_id_generation.yaml")
111
- runner = CliRunner()
112
- result = runner.invoke(cli, ["validate", test_yaml], catch_exceptions=False)
113
- self.assertNotEqual(result.exit_code, 0)
114
-
115
- def test_validation_flags(self) -> None:
116
- """Test schema validation flags."""
117
- runner = CliRunner()
118
- result = runner.invoke(
119
- cli,
120
- [
121
- "validate",
122
- "--check-description",
123
- "--check-tap-principal",
124
- "--check-tap-table-indexes",
125
- TEST_YAML,
126
- ],
127
- catch_exceptions=False,
128
- )
129
- self.assertEqual(result.exit_code, 0)
130
-
131
- def test_initialize_and_drop(self) -> None:
132
- """Test that initialize and drop can't be used together."""
133
- runner = CliRunner()
134
- result = runner.invoke(
135
- cli,
136
- ["create", "--initialize", "--drop", TEST_YAML],
137
- catch_exceptions=False,
138
- )
139
- self.assertTrue(result.exit_code != 0)
140
-
141
- def test_load_tap_schema(self) -> None:
142
- """Test load-tap-schema command."""
143
- # Create the TAP_SCHEMA database.
144
- url = f"sqlite:///{self.tmpdir}/load_tap_schema.sqlite3"
145
- runner = CliRunner()
146
- tap_schema_path = tap_schema.TableManager.get_tap_schema_std_path()
147
- result = runner.invoke(
148
- cli,
149
- ["--id-generation", "create", f"--engine-url={url}", tap_schema_path],
150
- catch_exceptions=False,
151
- )
152
- self.assertEqual(result.exit_code, 0)
153
-
154
- # Load the TAP_SCHEMA data.
155
- runner = CliRunner()
156
- result = runner.invoke(
157
- cli, ["load-tap-schema", f"--engine-url={url}", TEST_YAML], catch_exceptions=False
158
- )
159
- self.assertEqual(result.exit_code, 0)
160
-
161
- def test_init_tap_schema(self) -> None:
162
- """Test init-tap-schema command."""
163
- url = f"sqlite:///{self.tmpdir}/init_tap_schema.sqlite3"
164
- runner = CliRunner()
165
- result = runner.invoke(cli, ["init-tap-schema", f"--engine-url={url}"], catch_exceptions=False)
166
- self.assertEqual(result.exit_code, 0)
167
-
168
- def test_init_tap_schema_mock(self) -> None:
169
- """Test init-tap-schema command with a mock URL, which should throw
170
- an error, as this is not supported.
171
- """
172
- runner = CliRunner()
173
- result = runner.invoke(cli, ["init-tap-schema", "sqlite://"], catch_exceptions=False)
174
- self.assertNotEqual(result.exit_code, 0)
175
-
176
- def test_diff(self) -> None:
177
- """Test for ``diff`` command."""
178
- test_diff1 = os.path.join(TESTDIR, "data", "test_diff1.yaml")
179
- test_diff2 = os.path.join(TESTDIR, "data", "test_diff2.yaml")
180
-
181
- runner = CliRunner()
182
- result = runner.invoke(cli, ["diff", test_diff1, test_diff2], catch_exceptions=False)
183
- self.assertEqual(result.exit_code, 0)
184
-
185
- def test_diff_database(self) -> None:
186
- """Test for ``diff`` command with database."""
187
- test_diff1 = os.path.join(TESTDIR, "data", "test_diff1.yaml")
188
- test_diff2 = os.path.join(TESTDIR, "data", "test_diff2.yaml")
189
- db_url = f"sqlite:///{self.tmpdir}/tap_schema.sqlite3"
190
-
191
- engine = create_engine(db_url)
192
- metadata_db = MetaDataBuilder(Schema.from_uri(test_diff1), apply_schema_to_metadata=False).build()
193
- metadata_db.create_all(engine)
194
-
195
- runner = CliRunner()
196
- result = runner.invoke(cli, ["diff", f"--engine-url={db_url}", test_diff2], catch_exceptions=False)
197
- self.assertEqual(result.exit_code, 0)
198
-
199
- def test_diff_alembic(self) -> None:
200
- """Test for ``diff`` command with ``--alembic`` comparator option."""
201
- test_diff1 = os.path.join(TESTDIR, "data", "test_diff1.yaml")
202
- test_diff2 = os.path.join(TESTDIR, "data", "test_diff2.yaml")
203
-
204
- runner = CliRunner()
205
- result = runner.invoke(
206
- cli, ["diff", "--comparator", "alembic", test_diff1, test_diff2], catch_exceptions=False
207
- )
208
- print(result.output)
209
- self.assertEqual(result.exit_code, 0)
210
-
211
- def test_diff_error(self) -> None:
212
- """Test for ``diff`` command with bad arguments."""
213
- test_diff1 = os.path.join(TESTDIR, "data", "test_diff1.yaml")
214
- runner = CliRunner()
215
- result = runner.invoke(cli, ["diff", test_diff1], catch_exceptions=False)
216
- self.assertNotEqual(result.exit_code, 0)
217
-
218
- def test_diff_error_on_change(self) -> None:
219
- """Test for ``diff`` command with ``--error-on-change`` flag."""
220
- test_diff1 = os.path.join(TESTDIR, "data", "test_diff1.yaml")
221
- test_diff2 = os.path.join(TESTDIR, "data", "test_diff2.yaml")
222
-
223
- runner = CliRunner()
224
- result = runner.invoke(
225
- cli, ["diff", "--error-on-change", test_diff1, test_diff2], catch_exceptions=False
226
- )
227
- print(result.output)
228
- self.assertNotEqual(result.exit_code, 0)
229
-
230
- def test_dump_yaml(self) -> None:
231
- """Test for ``dump`` command with YAML output."""
232
- runner = CliRunner()
233
- with tempfile.NamedTemporaryFile(delete=False, suffix=".yaml") as temp_file:
234
- temp_file_name = temp_file.name
235
- try:
236
- result = runner.invoke(cli, ["dump", TEST_YAML, temp_file_name], catch_exceptions=False)
237
- print(result.output)
238
- self.assertEqual(result.exit_code, 0)
239
- finally:
240
- os.remove(temp_file_name)
241
-
242
- def test_dump_json(self) -> None:
243
- """Test for ``dump`` command with JSON output."""
244
- runner = CliRunner()
245
- with tempfile.NamedTemporaryFile(delete=False, suffix=".json") as temp_file:
246
- temp_file_name = temp_file.name
247
- try:
248
- result = runner.invoke(cli, ["dump", TEST_YAML, temp_file_name], catch_exceptions=False)
249
- print(result.output)
250
- self.assertEqual(result.exit_code, 0)
251
- finally:
252
- os.remove(temp_file_name)
253
-
254
- def test_dump_invalid_file_extension(self) -> None:
255
- """Test for ``dump`` command with JSON output."""
256
- runner = CliRunner()
257
- result = runner.invoke(cli, ["dump", TEST_YAML, "out.bad"], catch_exceptions=False)
258
- print(result.output)
259
- self.assertNotEqual(result.exit_code, 0)
260
-
261
-
262
- if __name__ == "__main__":
263
- unittest.main()