From 6836c4575366fcfeeb9d5ad2f517c5475c5eae64 Mon Sep 17 00:00:00 2001 From: zhao vistart Date: Sun, 8 Nov 2026 19:28:33 +0800 Subject: [PATCH 1/6] feat(sqlserver): render seconds_between with DATEDIFF_BIG --- README.md | 24 +++ .../backend/impl/sqlserver/dialect.py | 1 + .../backend/impl/sqlserver/mixins/datetime.py | 31 +++ .../expression/test_seconds_between.py | 200 ++++++++++++++++++ 4 files changed, 256 insertions(+) create mode 100644 tests/rhosocial/activerecord_sqlserver_test/feature/backend/expression/test_seconds_between.py diff --git a/README.md b/README.md index 3898845..93eb8a3 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,30 @@ CreateTableStatement( ) ``` +### Signed seconds between two instants + +`seconds_between(start, end)` answers `end - start` in seconds, keeping the +sign and the fraction. SQL Server has no interval subtraction, so the backend +counts milliseconds with `DATEDIFF_BIG` and divides by `1000.0`: + +```python +from rhosocial.activerecord.backend.expression import ( + Column, + seconds_between, +) + +elapsed = seconds_between(dialect, Column(dialect, "started_at"), + Column(dialect, "ended_at")).as_("seconds") +# DATEDIFF_BIG(MILLISECOND, [started_at], [ended_at]) / 1000.0 AS [seconds] +``` + +`DATEDIFF_BIG` rather than `DATEDIFF` because a millisecond count overflows a +32-bit `int` after about 24 days; the result is exact to a millisecond, which +is the resolution of `DATEDIFF_BIG`, not of `datetime2`. The function arrived +in SQL Server 2016; a server below that floor refuses the expression, and +`supports_functions()["seconds_between"]` reports `False`. An `end` earlier +than `start` subtracts to a negative value with no extra sign handling. + ## Version Gates Gates below are the versions at which this backend's own `supports_*` diff --git a/src/rhosocial/activerecord/backend/impl/sqlserver/dialect.py b/src/rhosocial/activerecord/backend/impl/sqlserver/dialect.py index 6950531..e6f0a97 100644 --- a/src/rhosocial/activerecord/backend/impl/sqlserver/dialect.py +++ b/src/rhosocial/activerecord/backend/impl/sqlserver/dialect.py @@ -525,6 +525,7 @@ class SQLServerDialect( "datetimeoffsetfromparts": (SQL_SERVER_2012, None), "timefromparts": (SQL_SERVER_2012, None), "datediff_big": (SQL_SERVER_2016, None), + "seconds_between": (SQL_SERVER_2016, None), "approx_count_distinct": (SQL_SERVER_2019, None), "json_path_exists": (SQL_SERVER_2022, None), "json_object": (SQL_SERVER_2022, None), diff --git a/src/rhosocial/activerecord/backend/impl/sqlserver/mixins/datetime.py b/src/rhosocial/activerecord/backend/impl/sqlserver/mixins/datetime.py index 73b665d..9f170b9 100644 --- a/src/rhosocial/activerecord/backend/impl/sqlserver/mixins/datetime.py +++ b/src/rhosocial/activerecord/backend/impl/sqlserver/mixins/datetime.py @@ -2,6 +2,7 @@ from typing import Any, Tuple, TYPE_CHECKING from rhosocial.activerecord.backend.dialect.exceptions import UnsupportedFeatureError +from .version_constants import SQL_SERVER_2016 if TYPE_CHECKING: from ..dialect import SQLServerDialect @@ -89,3 +90,33 @@ def format_datetime_diff_expression(self, expr: "Any") -> Tuple[str, tuple]: end_sql, end_params = expr.end.to_sql() sql = f"DATEDIFF({expr.unit.value.upper()}, {start_sql}, {end_sql})" return self.apply_alias(sql, start_params + end_params, expr) + + def format_seconds_between_expression(self, expr: "Any") -> Tuple[str, tuple]: + """Format signed seconds as ``DATEDIFF_BIG`` milliseconds over 1000.0. + + ``DATEDIFF_BIG`` counts the millisecond boundaries crossed from the + first instant to the second, so ``end`` before ``start`` is already a + negative count and no sign correction is needed. ``BIG`` rather than + plain ``DATEDIFF`` because a millisecond count overflows ``int`` after + roughly 24 days, and a span between two timestamps is routinely longer; + it has been available since SQL Server 2016. + + Dividing by ``1000.0`` keeps the millisecond part rather than truncating + to whole seconds. A second is stored with sub-millisecond precision in + ``datetime2``, but ``DATEDIFF_BIG`` counts milliseconds, so the rendered + value is exact to a millisecond and the finer part is the engine's + rounding, not this node's. + + The SQL writes ``start`` before ``end`` -- that is the argument order + ``DATEDIFF_BIG`` takes -- so the parameters are collected in that order. + """ + if self.version < SQL_SERVER_2016: + raise UnsupportedFeatureError( + self.name, + "seconds_between(start, end)", + "DATEDIFF_BIG requires SQL Server 2016 or later.", + ) + start_sql, start_params = expr.start.to_sql() + end_sql, end_params = expr.end.to_sql() + sql = f"DATEDIFF_BIG(MILLISECOND, {start_sql}, {end_sql}) / 1000.0" + return self.apply_alias(sql, start_params + end_params, expr) diff --git a/tests/rhosocial/activerecord_sqlserver_test/feature/backend/expression/test_seconds_between.py b/tests/rhosocial/activerecord_sqlserver_test/feature/backend/expression/test_seconds_between.py new file mode 100644 index 0000000..175c6e8 --- /dev/null +++ b/tests/rhosocial/activerecord_sqlserver_test/feature/backend/expression/test_seconds_between.py @@ -0,0 +1,200 @@ +# tests/rhosocial/activerecord_sqlserver_test/feature/backend/expression/test_seconds_between.py +"""``seconds_between`` on SQL Server: ``DATEDIFF_BIG`` milliseconds over 1000. + +The node answers a question -- the signed, fractional distance from one instant +to another -- and this backend spells it as a millisecond count divided by +``1000.0``. The offline cases pin the SQL text, the bind order and the alias; +the live cases execute the same statement so the sign, the zero, the fraction +and the calendar boundary are measured rather than assumed. +""" + +import pytest + +from rhosocial.activerecord.backend.dialect.exceptions import UnsupportedFeatureError +from rhosocial.activerecord.backend.expression import ( + Column, + Literal, + QueryStatement, + seconds_between, +) +from rhosocial.activerecord.backend.expression.column_types import TimestampColumn +from rhosocial.activerecord.backend.expression.datetime import SecondsBetweenExpression +from rhosocial.activerecord.backend.expression.value_types import NumericValue +from rhosocial.activerecord.backend.impl.sqlserver.dialect import SQLServerDialect +from rhosocial.activerecord.backend.options import ExecutionOptions +from rhosocial.activerecord.backend.schema import StatementType + +DQL_OPTIONS = ExecutionOptions(stmt_type=StatementType.DQL) + + +@pytest.fixture +def dialect(): + """A SQL Server 2022 dialect.""" + return SQLServerDialect(version=(16, 0, 0)) + + +class TestSecondsBetweenSqlText: + """The spelling, without a server.""" + + def test_columns(self, dialect): + expr = seconds_between( + dialect, + Column(dialect, "started_at"), + Column(dialect, "ended_at"), + ) + + sql, params = expr.to_sql() + + assert sql == ( + "DATEDIFF_BIG(MILLISECOND, [started_at], [ended_at]) / 1000.0" + ) + assert params == () + + def test_literal_params_follow_sql_order(self, dialect): + expr = seconds_between( + dialect, + Literal(dialect, "2026-01-01 00:00:00"), + Literal(dialect, "2026-01-02 00:00:00"), + ) + + sql, params = expr.to_sql() + + assert sql == "DATEDIFF_BIG(MILLISECOND, ?, ?) / 1000.0" + # ``start`` is the first argument ``DATEDIFF_BIG`` takes, so it binds + # first -- the SQL order, not the constructor's labels. + assert params == ("2026-01-01 00:00:00", "2026-01-02 00:00:00") + + def test_alias_is_applied_after_the_division(self, dialect): + expr = seconds_between( + dialect, + Column(dialect, "started_at"), + Column(dialect, "ended_at"), + ).as_("elapsed") + + sql, params = expr.to_sql() + + assert sql == ( + "DATEDIFF_BIG(MILLISECOND, [started_at], [ended_at]) / 1000.0 " + "AS [elapsed]" + ) + assert params == () + + def test_alias_keeps_bind_order(self, dialect): + expr = seconds_between( + dialect, + Literal(dialect, "2026-01-01 00:00:00"), + Literal(dialect, "2026-01-02 00:00:00"), + ).as_("elapsed") + + sql, params = expr.to_sql() + + assert sql == "DATEDIFF_BIG(MILLISECOND, ?, ?) / 1000.0 AS [elapsed]" + assert params == ("2026-01-01 00:00:00", "2026-01-02 00:00:00") + + def test_factory_coerces_string_to_column(self, dialect): + expr = seconds_between(dialect, "started_at", "ended_at") + + sql, params = expr.to_sql() + + assert sql == ( + "DATEDIFF_BIG(MILLISECOND, [started_at], [ended_at]) / 1000.0" + ) + assert params == () + + def test_column_method(self, dialect): + started = TimestampColumn(dialect, "started_at") + ended = TimestampColumn(dialect, "ended_at") + + sql, params = started.seconds_between(ended).to_sql() + + assert sql == ( + "DATEDIFF_BIG(MILLISECOND, [started_at], [ended_at]) / 1000.0" + ) + assert params == () + + def test_node_is_a_fractional_number(self, dialect): + expr = seconds_between( + dialect, + Column(dialect, "started_at"), + Column(dialect, "ended_at"), + ) + + assert isinstance(expr, SecondsBetweenExpression) + assert isinstance(expr, NumericValue) + assert expr.format_method == "format_seconds_between_expression" + + +class TestSecondsBetweenVersionFloor: + """``DATEDIFF_BIG`` arrived in SQL Server 2016, and the backend says so.""" + + def test_before_2016_refuses(self): + dialect = SQLServerDialect(version=(12, 0, 0)) + expr = seconds_between( + dialect, + Column(dialect, "started_at"), + Column(dialect, "ended_at"), + ) + + with pytest.raises(UnsupportedFeatureError) as excinfo: + expr.to_sql() + + assert "DATEDIFF_BIG" in str(excinfo.value) + assert "2016" in str(excinfo.value) + + def test_support_probe_reports_the_floor(self): + old = SQLServerDialect(version=(12, 0, 0)) + new = SQLServerDialect(version=(13, 0, 0)) + + assert old.supports_functions().get("seconds_between") is False + assert new.supports_functions().get("seconds_between") is True + + +def _elapsed(backend, start: str, end: str) -> float: + """Run ``seconds_between(start, end)`` and return the scalar.""" + dialect = backend.dialect + expr = seconds_between( + dialect, + Literal(dialect, start), + Literal(dialect, end), + ) + query = QueryStatement(dialect, select=[expr.as_("seconds")]) + sql, params = query.to_sql() + result = backend.execute(sql, params, options=DQL_OPTIONS) + # ``DATEDIFF_BIG(...) / 1000.0`` is numeric, which pyodbc returns as + # ``Decimal``; the assertions compare the value, not the wrapper. + return float(result.data[0]["seconds"]) + + +class TestSecondsBetweenExecution: + """The value SQL Server actually produces.""" + + @pytest.mark.parametrize( + "start,end,expected", + [ + ("2026-06-04 10:00:00", "2026-06-04 10:00:00", 0.0), + ("2026-06-04 10:00:00", "2026-06-04 11:00:00", 3600.0), + ("2026-06-04 11:00:00", "2026-06-04 10:00:00", -3600.0), + ("2026-01-31 23:59:59", "2026-02-01 00:00:00", 1.0), + ("2025-12-31 12:00:00", "2026-01-01 12:00:00", 86400.0), + ("2026-06-04 10:00:00.000", "2026-06-04 10:00:00.500", 0.5), + ("2026-06-04 10:00:00.250", "2026-06-04 10:00:00.000", -0.25), + ], + ) + def test_interval(self, sqlserver_backend, start: str, end: str, expected: float): + assert _elapsed(sqlserver_backend, start, end) == pytest.approx( + expected, abs=1e-3 + ) + + def test_span_longer_than_an_int_millisecond_count(self, sqlserver_backend): + """``DATEDIFF_BIG`` survives a span plain ``DATEDIFF`` overflows. + + A millisecond count above ``2**31 - 1`` is roughly 24.8 days, so 60 + days is past the limit that makes the ``BIG`` spelling necessary. + """ + seconds = _elapsed( + sqlserver_backend, + "2026-01-01 00:00:00", + "2026-03-02 00:00:00", + ) + + assert seconds == pytest.approx(60 * 86400.0, abs=1e-3) From 0d25b71f8838e0e5d168c08d6dab9b4b0d6911a9 Mon Sep 17 00:00:00 2001 From: zhao vistart Date: Sun, 8 Nov 2026 20:03:43 +0800 Subject: [PATCH 2/6] chore(ci): test against the core extract-epoch branch --- .github/workflows/test.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2f10504..154c7fc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -54,7 +54,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -144,7 +144,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -234,7 +234,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -324,7 +324,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -414,7 +414,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -504,7 +504,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -595,7 +595,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -685,7 +685,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -784,7 +784,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies From 121063dbd1feac5ef587ef7360f4962f2b9c5fb3 Mon Sep 17 00:00:00 2001 From: zhao vistart Date: Sun, 8 Nov 2026 20:07:52 +0800 Subject: [PATCH 3/6] chore: add changelog fragment for PR #11 --- changelog.d/11.added.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelog.d/11.added.md diff --git a/changelog.d/11.added.md b/changelog.d/11.added.md new file mode 100644 index 0000000..a04cf2d --- /dev/null +++ b/changelog.d/11.added.md @@ -0,0 +1,2 @@ +Render `seconds_between` as `DATEDIFF_BIG(MILLISECOND, ...) / 1000.0`, refusing below SQL Server 2016. + From 572a67864b3d93eb9a31ebd5936acc659e8e6b4d Mon Sep 17 00:00:00 2001 From: zhao vistart Date: Sun, 8 Nov 2026 20:11:45 +0800 Subject: [PATCH 4/6] chore(ci): annotate the paired core install as intentional --- .github/workflows/test.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 154c7fc..6ad207c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -54,7 +54,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -144,7 +144,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -234,7 +234,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -324,7 +324,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -414,7 +414,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -504,7 +504,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -595,7 +595,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -685,7 +685,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies @@ -784,7 +784,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" - name: Install dependencies From 306628efeadb40f2e6c7338c7e1ef023ee263899 Mon Sep 17 00:00:00 2001 From: zhao vistart Date: Sun, 8 Nov 2026 22:03:13 +0800 Subject: [PATCH 5/6] chore(ci): test against the testsuite extract-epoch branch --- .github/workflows/test.yml | 18 +++++++++--------- .../test_seconds_between_contract.py | 2 ++ 2 files changed, 11 insertions(+), 9 deletions(-) create mode 100644 tests/rhosocial/activerecord_sqlserver_test/feature/query/typed_column/test_seconds_between_contract.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6ad207c..d1b759b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,7 +55,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR - pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" + pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies run: | @@ -145,7 +145,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR - pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" + pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies run: | @@ -235,7 +235,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR - pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" + pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies run: | @@ -325,7 +325,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR - pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" + pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies run: | @@ -415,7 +415,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR - pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" + pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies run: | @@ -505,7 +505,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR - pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" + pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies run: | @@ -596,7 +596,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR - pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" + pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies run: | @@ -686,7 +686,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR - pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" + pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies run: | @@ -785,7 +785,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR - pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev17" + pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies run: | diff --git a/tests/rhosocial/activerecord_sqlserver_test/feature/query/typed_column/test_seconds_between_contract.py b/tests/rhosocial/activerecord_sqlserver_test/feature/query/typed_column/test_seconds_between_contract.py new file mode 100644 index 0000000..0974d21 --- /dev/null +++ b/tests/rhosocial/activerecord_sqlserver_test/feature/query/typed_column/test_seconds_between_contract.py @@ -0,0 +1,2 @@ +# tests/rhosocial/activerecord_sqlserver_test/feature/query/typed_column/test_seconds_between_contract.py +from rhosocial.activerecord.testsuite.feature.query.typed_column.test_seconds_between_contract import * # noqa: F401,F403 From 63a4e9de9b1fce1dec1d9ee4682e58e7a9118e8b Mon Sep 17 00:00:00 2001 From: zhao vistart Date: Sun, 8 Nov 2026 22:18:47 +0800 Subject: [PATCH 6/6] chore(ci): test against the merged core release --- .github/workflows/test.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d1b759b..36306e6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -54,7 +54,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies @@ -144,7 +144,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies @@ -234,7 +234,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies @@ -324,7 +324,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies @@ -414,7 +414,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies @@ -504,7 +504,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies @@ -595,7 +595,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies @@ -685,7 +685,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies @@ -784,7 +784,7 @@ jobs: - name: Install activerecord and testsuite from PyPI run: | - pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@feature/extract-epoch" # NOSONAR + pip install "rhosocial-activerecord @ git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev30" # NOSONAR pip install "rhosocial-activerecord-testsuite @ git+https://github.com/rhosocial/python-activerecord-testsuite.git@feature/extract-epoch" - name: Install dependencies