diff --git a/.azure/azure-pipelines.yml b/.azure/azure-pipelines.yml deleted file mode 100644 index 7e894cb..0000000 --- a/.azure/azure-pipelines.yml +++ /dev/null @@ -1,62 +0,0 @@ -jobs: - - job: "Test" - pool: - vmImage: "ubuntu-latest" - strategy: - matrix: - Python38: - python.version: "3.8" - Python39: - python.version: "3.9" - Python310: - python.version: "3.10" - Python311: - python.version: "3.11" - maxParallel: 4 - - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: "$(python.version)" - architecture: "x64" - - - script: curl -sSL https://install.python-poetry.org | python - displayName: Install Poetry - - - script: | - poetry install - displayName: "Install dependencies" - - - script: | - poetry run pylint --rcfile=pylintrc simple_ado - poetry run pylint --rcfile=pylintrc tests - displayName: "Lint" - - - script: | - poetry run mypy --ignore-missing-imports simple_ado/ - poetry run mypy --ignore-missing-imports tests/ - displayName: "Type Check" - - # Disable for now until we set up a testable Azure DevOps instance - # - script: | - # poetry run pytest tests --cov=simple_ado --cov-report html --cov-report xml --doctest-modules --junitxml=junit/test-results.xml - # env: - # SIMPLE_ADO_BASE_TOKEN: $(SIMPLE_ADO_BASE_TOKEN) - # displayName: "pytest" - - # - script: | - # poetry run inlinecss.py htmlcov - # displayName: "Inline CSS" - - # - task: PublishTestResults@2 - # inputs: - # testResultsFiles: "**/test-results.xml" - # testRunTitle: "Python $(python.version)" - # condition: succeededOrFailed() - - # # Publish Code Coverage Results - # - task: PublishCodeCoverageResults@1 - # inputs: - # codeCoverageTool: "cobertura" - # summaryFileLocation: $(System.DefaultWorkingDirectory)/coverage.xml - # reportDirectory: $(System.DefaultWorkingDirectory)/htmlcov diff --git a/.azure/publish.yml b/.azure/publish.yml deleted file mode 100644 index 71f47da..0000000 --- a/.azure/publish.yml +++ /dev/null @@ -1,41 +0,0 @@ -trigger: - tags: - include: - - "*" - -pool: - vmImage: "ubuntu-latest" - -jobs: - - job: "Publish" - pool: - vmImage: "ubuntu-latest" - - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: "3.x" - - - script: | - curl -sSL https://install.python-poetry.org | python3 - - displayName: "Install Poetry" - - - script: | - poetry install - displayName: "Install dependencies" - - - script: | - poetry build - displayName: "Build" - - - task: EsrpRelease@2 - inputs: - ConnectedServiceName: "OM-ESRP-Release-Publishing" - Intent: "PackageDistribution" - ContentType: "PyPi" - PackageLocation: "dist" - Owners: "jizhen@microsoft.com" - Approvers: "jizhen@microsoft.com" - ServiceEndpointUrl: "https://api.esrp.microsoft.com" - MainPublisher: "OutlookMobileiOSESRPPublisher" - DomainTenantId: "72f988bf-86f1-41af-91ab-2d7cd011db47" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..15db39d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,171 @@ +name: CI + +on: + push: + branches: [ main, master, develop ] + pull_request: + branches: [ main, master, develop ] + workflow_dispatch: + +jobs: + test: + name: Test Python ${{ matrix.python-version }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.10", "3.11", "3.12", "3.13"] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + version: latest + virtualenvs-create: true + virtualenvs-in-project: true + + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@v4 + with: + path: .venv + key: venv-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }} + + - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' + run: poetry install --no-interaction --no-root + + - name: Install project + run: poetry install --no-interaction + + - name: Run black check + run: poetry run black --check --line-length 100 simple_ado tests + + - name: Run pylint + run: | + poetry run pylint --rcfile=pylintrc simple_ado + poetry run pylint --rcfile=pylintrc tests + + - name: Run mypy + run: | + poetry run mypy --ignore-missing-imports simple_ado/ + poetry run mypy --ignore-missing-imports tests/ + + - name: Run unit tests + run: poetry run pytest tests/unit/ --cov=simple_ado --cov-report=xml --cov-report=html --junitxml=junit/test-results.xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + if: matrix.python-version == '3.12' + with: + file: ./coverage.xml + fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-results-${{ matrix.python-version }} + path: junit/test-results.xml + + - name: Upload coverage HTML report + uses: actions/upload-artifact@v4 + if: always() + with: + name: coverage-html-${{ matrix.python-version }} + path: htmlcov/ + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + if: always() + with: + file: ./coverage.xml + flags: unittests + name: codecov-umbrella + fail_ci_if_error: false + + deploy-coverage: + name: Deploy Coverage to GitHub Pages + needs: test + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + runs-on: ubuntu-latest + + permissions: + pages: write + id-token: write + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Download all coverage HTML reports + uses: actions/download-artifact@v4 + with: + pattern: coverage-html-* + path: coverage-artifacts + + - name: Select latest Python version coverage + run: | + # Find the coverage report from the highest Python version + LATEST_DIR=$(ls -d coverage-artifacts/coverage-html-* | sort -V | tail -1) + echo "Using coverage from: $LATEST_DIR" + mv "$LATEST_DIR" htmlcov + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: htmlcov + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + + integration-tests: + name: Integration Tests + runs-on: ubuntu-latest + # Only run if ADO credentials are available + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + version: latest + virtualenvs-create: true + virtualenvs-in-project: true + + - name: Install dependencies + run: poetry install --no-interaction + + - name: Run integration tests + run: poetry run pytest tests/ --integration --junitxml=junit/integration-results.xml + env: + SIMPLE_ADO_BASE_TOKEN: ${{ secrets.SIMPLE_ADO_BASE_TOKEN }} + SIMPLE_ADO_TENANT: ${{ secrets.SIMPLE_ADO_TENANT }} + SIMPLE_ADO_PROJECT_ID: ${{ secrets.SIMPLE_ADO_PROJECT_ID }} + SIMPLE_ADO_REPO_ID: ${{ secrets.SIMPLE_ADO_REPO_ID }} + + - name: Upload integration test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: integration-test-results + path: junit/integration-results.xml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..7650d49 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,122 @@ +name: Publish to PyPI + +on: + release: + types: [published] + workflow_dispatch: + inputs: + publish: + description: 'Publish to PyPI' + required: true + type: boolean + default: false + +jobs: + build: + name: Build distribution + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + version: latest + + - name: Install dependencies + run: poetry install --no-interaction + + - name: Build package + run: poetry build + + - name: Store the distribution packages + uses: actions/upload-artifact@v4 + with: + name: python-package-distributions + path: dist/ + + publish-to-pypi: + name: Publish to PyPI + if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.publish == 'true') + needs: + - build + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/simple-ado + permissions: + id-token: write # IMPORTANT: mandatory for trusted publishing + + steps: + - name: Download all the dists + uses: actions/download-artifact@v4 + with: + name: python-package-distributions + path: dist/ + + - name: Publish distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + + publish-to-testpypi: + name: Publish to TestPyPI + if: github.event_name == 'workflow_dispatch' && github.event.inputs.publish != 'true' + needs: + - build + runs-on: ubuntu-latest + environment: + name: testpypi + url: https://test.pypi.org/p/simple-ado + permissions: + id-token: write # IMPORTANT: mandatory for trusted publishing + + steps: + - name: Download all the dists + uses: actions/download-artifact@v4 + with: + name: python-package-distributions + path: dist/ + + - name: Publish distribution to TestPyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + + github-release: + name: Create GitHub Release + if: github.event_name == 'release' + needs: + - publish-to-pypi + runs-on: ubuntu-latest + permissions: + contents: write # IMPORTANT: mandatory for creating releases + id-token: write # IMPORTANT: mandatory for sigstore + + steps: + - name: Download all the dists + uses: actions/download-artifact@v4 + with: + name: python-package-distributions + path: dist/ + + - name: Sign the dists with Sigstore + uses: sigstore/gh-action-sigstore-python@v3.0.0 + with: + inputs: >- + ./dist/*.tar.gz + ./dist/*.whl + + - name: Upload artifact signatures to GitHub Release + env: + GITHUB_TOKEN: ${{ github.token }} + # Upload to GitHub Release using the `gh` CLI. + run: >- + gh release upload + '${{ github.ref_name }}' dist/** + --repo '${{ github.repository }}' diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..06e483f --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,75 @@ +name: Security & Dependency Check + +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] + schedule: + # Run weekly on Monday at 3 AM UTC + - cron: '0 3 * * 1' + workflow_dispatch: + +jobs: + security-check: + name: Security & Dependency Audit + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + version: latest + + - name: Install dependencies + run: poetry install --no-interaction + + - name: Check for security vulnerabilities with Safety + run: | + pip install safety + poetry export -f requirements.txt --without-hashes | safety check --stdin || true + continue-on-error: true + + - name: Run Bandit security linter + run: | + pip install bandit + bandit -r simple_ado/ -f json -o bandit-report.json || true + continue-on-error: true + + - name: Upload Bandit report + uses: actions/upload-artifact@v4 + if: always() + with: + name: bandit-security-report + path: bandit-report.json + + - name: Check Poetry lock file + run: poetry check --lock + + - name: Audit dependencies with pip-audit + run: | + pip install pip-audit + poetry export -f requirements.txt --without-hashes | pip-audit -r /dev/stdin || true + continue-on-error: true + + dependency-review: + name: Dependency Review + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Dependency Review + uses: actions/dependency-review-action@v4 + with: + fail-on-severity: moderate diff --git a/poetry.lock b/poetry.lock index fae35d1..48a3c6a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -162,13 +162,13 @@ files = [ [[package]] name = "click" -version = "8.1.8" +version = "8.3.0" description = "Composable command line interface toolkit" optional = false -python-versions = ">=3.7" +python-versions = ">=3.10" files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, + {file = "click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc"}, + {file = "click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4"}, ] [package.dependencies] @@ -361,29 +361,6 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] -[[package]] -name = "importlib-metadata" -version = "8.7.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.9" -files = [ - {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, - {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, -] - -[package.dependencies] -zipp = ">=3.20" - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["pytest-mypy"] - [[package]] name = "iniconfig" version = "2.1.0" @@ -406,9 +383,6 @@ files = [ {file = "isort-6.1.0.tar.gz", hash = "sha256:9b8f96a14cfee0677e78e941ff62f03769a06d412aabb9e2a90487b3b7e8d481"}, ] -[package.dependencies] -importlib-metadata = {version = ">=4.6.0", markers = "python_version < \"3.10\""} - [package.extras] colors = ["colorama"] plugins = ["setuptools"] @@ -586,7 +560,6 @@ mccabe = ">=0.6,<0.8" platformdirs = ">=2.2" tomli = {version = ">=1.1", markers = "python_version < \"3.11\""} tomlkit = ">=0.10.1" -typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} [package.extras] spelling = ["pyenchant (>=3.2,<4.0)"] @@ -944,26 +917,7 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] -[[package]] -name = "zipp" -version = "3.23.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.9" -files = [ - {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, - {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] -type = ["pytest-mypy"] - [metadata] lock-version = "2.0" -python-versions = "^3.9" -content-hash = "dea1257c2612c60b58561c1181b7621c4d5074426ab15d62b86e2a5f6cb91da8" +python-versions = "^3.10" +content-hash = "2a49b832a89c0c38af9637f2fd8e0a647781315db6e4e5d9b3fa3bfd3c01d9c7" diff --git a/pyproject.toml b/pyproject.toml index 8e205ab..322f412 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,16 +20,16 @@ classifiers = [ 'Environment :: MacOS X', 'Intended Audience :: Developers', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', 'Topic :: Software Development', 'Topic :: Utilities', ] [tool.poetry.dependencies] -python = "^3.9" +python = "^3.10" deserialize = "^2.0.1" requests = "^2.31.0" tenacity = "^8.2.2" diff --git a/tests/conftest.py b/tests/conftest.py index 48adda0..41b225a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,64 +14,62 @@ FIXTURES_DIR = Path(__file__).parent / "fixtures" -@pytest.fixture -def mock_tenant() -> str: +@pytest.fixture(name="mock_tenant") +def fixture_mock_tenant() -> str: """Return a mock tenant name.""" return "test-tenant" -@pytest.fixture -def mock_project_id() -> str: +@pytest.fixture(name="mock_project_id") +def fixture_mock_project_id() -> str: """Return a mock project ID.""" return "test-project-123" -@pytest.fixture -def mock_repository_id() -> str: +@pytest.fixture(name="mock_repository_id") +def fixture_mock_repository_id() -> str: """Return a mock repository ID.""" return "test-repo-456" -@pytest.fixture -def mock_feed_id() -> str: +@pytest.fixture(name="mock_feed_id") +def fixture_mock_feed_id() -> str: """Return a mock feed ID.""" return "test-feed-789" -@pytest.fixture -def mock_auth() -> ADOTokenAuth: +@pytest.fixture(name="mock_auth") +def fixture_mock_auth() -> ADOTokenAuth: """Return a mock authentication object.""" return ADOTokenAuth("mock-token-12345") -@pytest.fixture -def mock_client(mock_tenant: str, mock_auth: ADOTokenAuth) -> ADOClient: +@pytest.fixture(name="mock_client") +def fixture_mock_client(mock_tenant: str, mock_auth: ADOTokenAuth) -> ADOClient: """Return a mock ADO client.""" return ADOClient(tenant=mock_tenant, auth=mock_auth) -@pytest.fixture -def load_fixture(): +@pytest.fixture(name="load_fixture") +def fixture_load_fixture(): """Load a JSON fixture file.""" + def _load(filename: str) -> Dict[str, Any]: fixture_path = FIXTURES_DIR / filename if not fixture_path.exists(): raise FileNotFoundError(f"Fixture not found: {fixture_path}") - with open(fixture_path, 'r', encoding='utf-8') as f: + with open(fixture_path, "r", encoding="utf-8") as f: return json.load(f) + return _load def pytest_configure(config): """Configure pytest with custom markers.""" config.addinivalue_line( - "markers", - "integration: marks tests as integration tests (require real ADO access)" - ) - config.addinivalue_line( - "markers", - "destructive: marks tests that modify ADO resources" + "markers", "integration: marks tests as integration tests (require real ADO access)" ) + config.addinivalue_line("markers", "destructive: marks tests that modify ADO resources") def pytest_collection_modifyitems(config, items): @@ -79,7 +77,7 @@ def pytest_collection_modifyitems(config, items): if config.getoption("--integration"): # Running with --integration flag, don't skip anything return - + skip_integration = pytest.mark.skip(reason="need --integration option to run") for item in items: if "integration" in item.keywords: @@ -92,14 +90,15 @@ def pytest_addoption(parser): "--integration", action="store_true", default=False, - help="run integration tests that require ADO access" + help="run integration tests that require ADO access", ) # Integration test fixtures (only used when --integration is specified) -@pytest.fixture -def integration_tenant() -> str: + +@pytest.fixture(name="integration_tenant") +def fixture_integration_tenant() -> str: """Get tenant from environment for integration tests.""" tenant = os.getenv("SIMPLE_ADO_TENANT") if not tenant: @@ -107,8 +106,8 @@ def integration_tenant() -> str: return tenant -@pytest.fixture -def integration_token() -> str: +@pytest.fixture(name="integration_token") +def fixture_integration_token() -> str: """Get token from environment for integration tests.""" token = os.getenv("SIMPLE_ADO_BASE_TOKEN") if not token: @@ -116,8 +115,8 @@ def integration_token() -> str: return token -@pytest.fixture -def integration_project_id() -> str: +@pytest.fixture(name="integration_project_id") +def fixture_integration_project_id() -> str: """Get project ID from environment for integration tests.""" project_id = os.getenv("SIMPLE_ADO_PROJECT_ID") if not project_id: @@ -125,8 +124,8 @@ def integration_project_id() -> str: return project_id -@pytest.fixture -def integration_repo_id() -> str: +@pytest.fixture(name="integration_repo_id") +def fixture_integration_repo_id() -> str: """Get repository ID from environment for integration tests.""" repo_id = os.getenv("SIMPLE_ADO_REPO_ID") if not repo_id: @@ -134,8 +133,8 @@ def integration_repo_id() -> str: return repo_id -@pytest.fixture -def integration_client(integration_tenant: str, integration_token: str) -> ADOClient: +@pytest.fixture(name="integration_client") +def fixture_integration_client(integration_tenant: str, integration_token: str) -> ADOClient: """Return a real ADO client for integration tests.""" auth = ADOTokenAuth(integration_token) return ADOClient(tenant=integration_tenant, auth=auth) diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index af99118..01b94a8 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -17,20 +17,20 @@ import simple_ado -@pytest.fixture -def client(integration_client): +@pytest.fixture(name="client") +def fixture_client(integration_client): """Return the integration client.""" return integration_client -@pytest.fixture -def project_id(integration_project_id): +@pytest.fixture(name="project_id") +def fixture_project_id(integration_project_id): """Return the integration project ID.""" return integration_project_id -@pytest.fixture -def repository_id(integration_repo_id): +@pytest.fixture(name="repository_id") +def fixture_repository_id(integration_repo_id): """Return the integration repository ID.""" return integration_repo_id @@ -42,6 +42,7 @@ def test_access(client): @pytest.mark.integration +@pytest.mark.destructive def test_get_blobs(client, project_id, repository_id): """Test get blobs.""" client.git.get_blobs( @@ -118,7 +119,6 @@ def test_properties(client, project_id, repository_id): @pytest.mark.integration -@pytest.mark.destructive def test_list_repos(client, project_id): """Test list repos.""" repos = client.git.all_repositories(project_id=project_id) diff --git a/tests/unit/test_builds.py b/tests/unit/test_builds.py index 5beb9d8..2f8d668 100644 --- a/tests/unit/test_builds.py +++ b/tests/unit/test_builds.py @@ -1,8 +1,9 @@ """Unit tests for the Builds client.""" -import pytest import responses -from simple_ado.builds import ADOBuildClient, BuildQueryOrder +from simple_ado.builds import BuildQueryOrder + +# pylint: disable=line-too-long @responses.activate @@ -39,7 +40,7 @@ def test_get_builds_with_definition_filter(mock_client, mock_project_id): builds = list(mock_client.builds.get_builds(project_id=mock_project_id, definitions=[100, 101])) - assert builds == [] + assert not builds @responses.activate @@ -63,7 +64,7 @@ def test_get_builds_with_order(mock_client, mock_project_id): ) ) - assert builds == [] + assert not builds @responses.activate diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index 61eeff7..99915fc 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -1,10 +1,11 @@ """Unit tests for the main ADOClient class.""" -import pytest import responses from simple_ado import ADOClient from simple_ado.auth import ADOTokenAuth +# pylint: disable=line-too-long + def test_client_initialization(mock_tenant, mock_auth): """Test that client initializes correctly."""