iris-devtester 1.19.2__tar.gz → 1.19.4__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.
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/CHANGELOG.md +20 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/CONSTITUTION.md +85 -14
- {iris_devtester-1.19.2/iris_devtester.egg-info → iris_devtester-1.19.4}/PKG-INFO +1 -1
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/__init__.py +1 -1
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/containers/cpf_manager.py +6 -3
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/containers/iris_container.py +76 -18
- {iris_devtester-1.19.2 → iris_devtester-1.19.4/iris_devtester.egg-info}/PKG-INFO +1 -1
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester.egg-info/SOURCES.txt +16 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/pyproject.toml +1 -1
- iris_devtester-1.19.4/tests/unit/test_adapter_langchain_diag.py +1028 -0
- iris_devtester-1.19.4/tests/unit/test_auto_discovery.py +304 -0
- iris_devtester-1.19.4/tests/unit/test_autodiscovery_dbapi.py +537 -0
- iris_devtester-1.19.4/tests/unit/test_config_models_unit.py +1327 -0
- iris_devtester-1.19.4/tests/unit/test_connections_unit.py +1423 -0
- iris_devtester-1.19.4/tests/unit/test_container_state.py +368 -0
- iris_devtester-1.19.4/tests/unit/test_container_validation.py +632 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_cpf_manager.py +4 -2
- iris_devtester-1.19.4/tests/unit/test_fixtures_extra.py +1034 -0
- iris_devtester-1.19.4/tests/unit/test_fixtures_unit.py +770 -0
- iris_devtester-1.19.4/tests/unit/test_health_checks.py +944 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_iris_container.py +79 -35
- iris_devtester-1.19.4/tests/unit/test_iris_container_extra.py +957 -0
- iris_devtester-1.19.4/tests/unit/test_monitoring_perf_diag.py +1466 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_password_preconfig.py +152 -2
- iris_devtester-1.19.4/tests/unit/test_password_utils.py +989 -0
- iris_devtester-1.19.4/tests/unit/test_ports.py +639 -0
- iris_devtester-1.19.4/tests/unit/test_progress.py +256 -0
- iris_devtester-1.19.4/tests/unit/test_utils_subprocess.py +810 -0
- iris_devtester-1.19.4/tests/unit/test_wait_dev_unit.py +1143 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_wait_strategies.py +1 -34
- iris_devtester-1.19.4/tests/unit/utils/test_dbapi_compat.py +555 -0
- iris_devtester-1.19.2/tests/unit/test_auto_discovery.py +0 -456
- iris_devtester-1.19.2/tests/unit/utils/test_dbapi_compat.py +0 -183
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/AGENTS.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/CLAUDE.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/LICENSE +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/MANIFEST.in +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/README.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/GETTING_STARTED.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/LANGCHAIN_INTEGRATION_QUICKSTART.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/ROADMAP.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/SQL_VS_OBJECTSCRIPT.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/TROUBLESHOOTING.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/WHATS_NEW_v1.4.0.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/archive/AGENTIC_SANDBOX_ENHANCEMENT_REPORT.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/archive/IMPACT_ANALYSIS.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/archive/IRIS_DEVTESTER_POSITIONING_REPORT.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/archive/LANGCHAIN_INTEGRATION_STRATEGY.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/archive/LANGCHAIN_INTEGRATION_TEST_RESULTS.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/archive/PHASE_2_PLAN.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/archive/RAG_TEMPLATES_ANALYSIS.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/archive/README.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/archive/STRATEGIC_ROADMAP_SUMMARY.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/archive/feature-009-refactor-plan.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/development/BUG_REPORT.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/development/EXTRACTION_SUMMARY.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/development/FEATURE_006_SUMMARY.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/development/PHASE2_RESULTS.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/development/PRE_PUBLISH_REVIEW.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/development/PROGRESS.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/development/PYPI_READINESS.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/development/SESSION-INTEGRATION-TESTS.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/development/SESSION-SUMMARY.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/development/STATUS-002-PIVOTED.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/development/STATUS-003-COMPLETE.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/development/STATUS.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/development/V1_COMPLETION_PLAN.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/development/V1_RELEASE_SUMMARY.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/features/WSGI-ASGI-SERVER-SETUP.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/features/agent-skills.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/features/dat-fixtures.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/features/docker-compose.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/features/performance-monitoring.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/features/testcontainers.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/api-contract-synchronization.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/callin-service-requirement.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/cpf-merge-and-container-restarts.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/dat-fixtures-docker-exec-pattern.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/dat-restore-database-isolation-problem.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/dbapi-bulk-insert-performance-issue.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/dbapi-objectscript-limitation.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/dbapi-password-reset-limitation.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/dbapi-private-module-myth.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/docker-hub-image-naming.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/embedded-python-considerations.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/feature-002-pivot-summary.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/integration-test-dbapi-limitation.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/iris-backup-patterns.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/iris-connection-access-denied-bug.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/iris-container-dind-port-mapping.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/iris-container-graceful-shutdown.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/iris-container-performance.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/iris-container-readiness.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/iris-container-volume-permissions.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/iris-docker-sdk-environment-variables.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/iris-performance-monitoring-landscape.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/iris-security-users-api.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/irishealth-editions.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/namespace-sql-vs-iris-connect.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/password-reset-changeflag-fix.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/rag-templates-production-patterns.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/sql-task-manager-operations.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/test-isolation-fix-summary.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/test-isolation-investigation.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/learnings/testcontainers-ryuk-lifecycle.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/docs/troubleshooting/table-not-found.md +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/examples/01_quickstart.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/examples/02_connection_management.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/examples/04_pytest_fixtures.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/examples/05_ci_cd.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/examples/08_auto_discovery.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/examples/09_enterprise.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/examples/10_docker_compose_integration.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/examples/langchain_integration_example.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/__main__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/cli/__init__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/cli/__main__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/cli/connection_commands.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/cli/container.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/cli/container_commands.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/cli/dev_commands.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/cli/fixture_commands.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/config/__init__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/config/auto_discovery.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/config/container_config.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/config/container_state.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/config/defaults.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/config/discovery.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/config/models.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/config/presets.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/config/yaml_loader.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/connections/__init__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/connections/auto_discovery.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/connections/connection.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/connections/cursor_wrapper.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/connections/dbapi.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/connections/jdbc.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/connections/manager.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/connections/models.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/connections/retry.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/containers/__init__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/containers/connection_info.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/containers/dev_instance.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/containers/models.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/containers/monitor_utils.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/containers/monitoring.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/containers/performance.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/containers/validation.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/containers/wait_strategies.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/diagnostics.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/fixtures/__init__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/fixtures/creator.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/fixtures/loader.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/fixtures/manifest.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/fixtures/obj_export.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/fixtures/validator.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/integrations/__init__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/integrations/langchain.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/ports/__init__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/ports/assignment.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/ports/exceptions.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/ports/registry.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/testing/__init__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/testing/fixtures.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/testing/helpers.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/testing/models.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/testing/schema_reset.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/utils/__init__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/utils/container_port.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/utils/container_status.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/utils/dbapi_compat.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/utils/enable_callin.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/utils/health_checks.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/utils/iris_container_adapter.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/utils/namespace.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/utils/password.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/utils/progress.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester/utils/test_connection.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester.egg-info/dependency_links.txt +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester.egg-info/entry_points.txt +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester.egg-info/requires.txt +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/iris_devtester.egg-info/top_level.txt +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/setup.cfg +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/__init__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/conftest.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/__init__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/cli/test_container_logs_contract.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/cli/test_container_remove_contract.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/cli/test_container_restart_contract.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/cli/test_container_start_contract.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/cli/test_container_status_contract.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/cli/test_container_stop_contract.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/cli/test_container_up_contract.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_031_diagnostics.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_032_health_check.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_033_health_editions.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_034_cpf_password.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_035_graceful_shutdown.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_agent_skills.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_cli_container_commands.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_cli_fixture_commands.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_cli_ux_contract.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_connection_api.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_container_api.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_container_integration_contract.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_container_status_api.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_container_validation_api.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_dind_port_mapping.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_enable_callin_api.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_fixture_creator_api.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_fixture_loader_api.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_fixture_validator_api.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_iriscontainer_attach.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_legacy_package_contract.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_modern_package_contract.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_monitoring_config_api.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_package_detection.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_port_registry_contract.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_preconfig_contract.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_reset_verification_contract.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_resource_monitoring_api.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_retry_logic_contract.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_task_manager_api.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_test_connection_api.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/contract/test_testing_fixtures_api.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/e2e/__init__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/e2e/test_port_registry_e2e.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/e2e/test_skill_workflow.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/__init__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/conftest.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/ports/__init__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/ports/test_manual_port_override.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/ports/test_multi_project_isolation.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/ports/test_port_exhaustion.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/ports/test_port_persistence.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/ports/test_stale_cleanup.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_bug_fixes.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_bug_fixes_011.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_cli_container_integration.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_connection_info_integration.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_connection_integration.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_container_health_monitor.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_container_status_integration.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_container_validation.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_cpf_merge.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_cpf_presets.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_cpf_security.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_custom_user_remediation.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_dev_instance.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_enable_callin_integration.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_fixture_export_patterns.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_fixture_performance.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_fixture_refresh.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_fixtures_integration.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_implicit_namespace.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_iriscontainer_attach_integration.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_monitor_utils_integration.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_monitoring_integration.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_password_preconfig_integration.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_password_reset_both_editions.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_password_reset_integration.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_password_reset_macos.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_password_reset_timing.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_password_reset_validation.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_proactive_password_reset.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_real_world_problems.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_reliable_password_reset.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/integration/test_test_connection_integration.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/__init__.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/config/test_config_hierarchy.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/config/test_container_config.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/config/test_yaml_loader.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_config_discovery.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_connection_fallback.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_connection_info.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_dev_instance_logic.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_iris_config.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_iris_connection_info.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_manifest.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_monitoring_policy.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_namespace_utils.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_password_preconfig_unit.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_performance_metrics.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_resource_thresholds.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_retry.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_schema_models.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_schema_reset_logic.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/test_validation_results.py +0 -0
- {iris_devtester-1.19.2 → iris_devtester-1.19.4}/tests/unit/utils/test_iris_container_adapter.py +0 -0
|
@@ -5,6 +5,26 @@ All notable changes to iris-devtester will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.19.4] - 2026-08-25 - Fix plaintext password in CPF merge + wildcard unexpire on attach
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **`start()` passes plaintext password into `PasswordHash=` CPF field** (`containers/iris_container.py`): when `with_preconfigured_password()` was used, `start()` built a custom CPF merge that set `PasswordHash={plaintext}`. The `PasswordHash=` field in a CPF merge file expects a `hash,salt` PBKDF2 format string — passing plaintext produces a corrupt stored hash that causes every subsequent login to fail silently with `<ACCESS VIOLATION>`. Fixed by always using `CPFPreset.SECURE_DEFAULTS` for the CPF merge (handles CallIn enablement and expiry flags correctly) and applying any custom password post-start via `reset_password()` which uses `PasswordExternal` — the IRIS Security API field that accepts plaintext and handles PBKDF2 hashing internally.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **`IRISContainer.attach(unexpire_passwords=True)`** (`containers/iris_container.py`): enterprise and HealthShare images commonly ship with expired `_SYSTEM` passwords or `ChangePassword=1` set. Without wildcard unexpire, the first programmatic login silently fails. `attach()` now calls `unexpire_all_passwords(container_name)` after successful container inspection by default. Failure is swallowed — the container is already attached and the caller may have valid credentials. Pass `unexpire_passwords=False` to skip when credentials are known-good and you want to avoid the overhead.
|
|
17
|
+
|
|
18
|
+
### Documentation
|
|
19
|
+
|
|
20
|
+
- **`CONSTITUTION.md` Principle 6** — added `_SYSTEM` Password Assumption section documenting: (1) community vs. enterprise default credential differences, (2) why `PasswordHash=` in CPF must never receive plaintext, (3) how IDT's CPF + post-start `reset_password()` dance works, and (4) the wildcard unexpire backstop in `attach()`.
|
|
21
|
+
|
|
22
|
+
## [1.19.3] - 2026-08-02 - Fix attach() ignoring explicit port override
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **`IRISContainer.attach()` ignores explicit port, always uses docker port lookup** (`containers/iris_container.py`): `attach()` had no port parameter. It always derived the host-side SuperServer port from `docker port <container> 1972/tcp`, which fails on macOS Docker Desktop with irishealth enterprise: Docker Desktop NAT rewrites the source IP from `127.0.0.1` to a bridge address, so IRIS RSTs the connection even though the port binding exists. The user-supplied `IVG_PORT=31971` was silently ignored. Added `port: Optional[int]` to `attach()`; when supplied, `_mapped_port` is pinned immediately so `get_mapped_port(1972)` returns it without calling `get_exposed_port()`. Also reads `IVG_PORT` and `IRIS_PORT` env vars as fallbacks when `port=` is not passed explicitly.
|
|
27
|
+
|
|
8
28
|
## [1.19.2] - 2026-07-30 - Fix connect_function ImportError when module absent from sys.modules
|
|
9
29
|
|
|
10
30
|
### Fixed
|
|
@@ -15,10 +15,12 @@ This constitution codifies the hard-won lessons, blind alleys avoided, and battl
|
|
|
15
15
|
**ABSOLUTE REQUIREMENT**: This project uses `intersystems-irispython` as the ONLY Python package for IRIS connectivity.
|
|
16
16
|
|
|
17
17
|
**FORBIDDEN PACKAGES**:
|
|
18
|
+
|
|
18
19
|
- ❌ `intersystems-iris` (old package, deprecated)
|
|
19
20
|
- ❌ Any other IRIS Python packages
|
|
20
21
|
|
|
21
22
|
**Why This Matters**:
|
|
23
|
+
|
|
22
24
|
- `intersystems-irispython` is the modern, maintained package (v5.3.0+)
|
|
23
25
|
- `intersystems-iris` is the legacy package (v3.0.0+) with known issues
|
|
24
26
|
- Mixing packages causes import conflicts and mysterious failures
|
|
@@ -35,6 +37,7 @@ This constitution codifies the hard-won lessons, blind alleys avoided, and battl
|
|
|
35
37
|
This single document answers the most critical question in IRIS development: "How do I execute this operation?"
|
|
36
38
|
|
|
37
39
|
**Why This Matters**:
|
|
40
|
+
|
|
38
41
|
- Using the wrong execution method causes mysterious failures
|
|
39
42
|
- 53 integration tests failed because of this misunderstanding
|
|
40
43
|
- DBAPI is 3x faster for SQL but CANNOT execute ObjectScript
|
|
@@ -50,11 +53,13 @@ This single document answers the most critical question in IRIS development: "Ho
|
|
|
50
53
|
**The Principle**: Infrastructure problems must be automatically detected and remediated without developer intervention.
|
|
51
54
|
|
|
52
55
|
**Why It Matters**:
|
|
56
|
+
|
|
53
57
|
- Password expiration errors have wasted hundreds of developer hours
|
|
54
58
|
- Manual remediation breaks CI/CD pipelines
|
|
55
59
|
- "Works on my machine" scenarios damage team productivity
|
|
56
60
|
|
|
57
61
|
**Implementation Requirements**:
|
|
62
|
+
|
|
58
63
|
- ✅ Password change required → automatic reset
|
|
59
64
|
- ✅ Container not found → automatic start
|
|
60
65
|
- ✅ Port conflicts → automatic port reassignment
|
|
@@ -62,11 +67,13 @@ This single document answers the most critical question in IRIS development: "Ho
|
|
|
62
67
|
- ✅ Connection failures → automatic retry with backoff
|
|
63
68
|
|
|
64
69
|
**Forbidden**:
|
|
70
|
+
|
|
65
71
|
- ❌ Error messages without remediation steps
|
|
66
72
|
- ❌ Requiring manual Docker commands
|
|
67
73
|
- ❌ Silent failures without auto-recovery attempts
|
|
68
74
|
|
|
69
75
|
**Example**:
|
|
76
|
+
|
|
70
77
|
```python
|
|
71
78
|
# WRONG: Manual intervention required
|
|
72
79
|
raise ConnectionError("Password change required. Run: docker exec...")
|
|
@@ -82,6 +89,7 @@ if "Password change required" in str(error):
|
|
|
82
89
|
**The Principle**: Use the appropriate connection method based on what operation you need to perform.
|
|
83
90
|
|
|
84
91
|
**Why It Matters**:
|
|
92
|
+
|
|
85
93
|
- IRIS has **two distinct execution paths**: SQL (via DBAPI) and ObjectScript (via iris.connect())
|
|
86
94
|
- Using the wrong tool causes mysterious failures
|
|
87
95
|
- DBAPI is 3x faster for SQL operations
|
|
@@ -90,17 +98,18 @@ if "Password change required" in str(error):
|
|
|
90
98
|
|
|
91
99
|
**The Decision Matrix**:
|
|
92
100
|
|
|
93
|
-
| Operation Type
|
|
94
|
-
|
|
95
|
-
| SQL queries (SELECT, INSERT, UPDATE, DELETE) | **DBAPI**
|
|
96
|
-
| SQL DDL (CREATE TABLE, DROP TABLE)
|
|
97
|
-
| Backup/Restore namespace
|
|
98
|
-
| Create/Delete namespace
|
|
99
|
-
| Task Manager operations
|
|
100
|
-
| Global variables
|
|
101
|
-
| User/password management
|
|
101
|
+
| Operation Type | Use | Speed | Example |
|
|
102
|
+
| -------------------------------------------- | --------------------------------- | --------- | ------------------------------------------------------------------ |
|
|
103
|
+
| SQL queries (SELECT, INSERT, UPDATE, DELETE) | **DBAPI** | Fast (3x) | `cursor.execute("SELECT * FROM MyTable")` |
|
|
104
|
+
| SQL DDL (CREATE TABLE, DROP TABLE) | **DBAPI** | Fast (3x) | `cursor.execute("CREATE TABLE ...")` |
|
|
105
|
+
| Backup/Restore namespace | **DBAPI** + $SYSTEM.OBJ.Execute() | Medium | `cursor.execute("SELECT $SYSTEM.OBJ.Execute('...')")` |
|
|
106
|
+
| Create/Delete namespace | **iris.connect()** | Medium | `iris_obj.classMethodValue("Config.Namespaces", "Create", "TEST")` |
|
|
107
|
+
| Task Manager operations | **iris.connect()** | Medium | `iris_obj.execute("Set task = ##class(%SYS.Task).%New()")` |
|
|
108
|
+
| Global variables | **iris.connect()** | Medium | `iris_obj.set("^MyGlobal", "value")` |
|
|
109
|
+
| User/password management | **docker exec** | Slow | `docker exec iris_db iris session IRIS ...` |
|
|
102
110
|
|
|
103
111
|
**Implementation Requirements**:
|
|
112
|
+
|
|
104
113
|
- ✅ Use DBAPI for all SQL operations (SELECT, INSERT, UPDATE, DELETE, CREATE TABLE)
|
|
105
114
|
- ✅ Use iris.connect() for ObjectScript operations (namespaces, Task Manager, globals)
|
|
106
115
|
- ✅ Use docker exec only for admin operations (password reset, system config)
|
|
@@ -108,6 +117,7 @@ if "Password change required" in str(error):
|
|
|
108
117
|
- ✅ Log which connection type is used for each operation
|
|
109
118
|
|
|
110
119
|
**Forbidden**:
|
|
120
|
+
|
|
111
121
|
- ❌ `cursor.execute("DO ##class(...)...")` - DBAPI cannot execute ObjectScript
|
|
112
122
|
- ❌ `cursor.execute("Set ^GlobalData = 'value'")` - DBAPI cannot set globals
|
|
113
123
|
- ❌ Using iris.connect() for simple SQL queries (3x slower than DBAPI)
|
|
@@ -144,6 +154,7 @@ cursor.execute("SELECT $SYSTEM.OBJ.Execute('Do ##class(Config.Namespaces).Create
|
|
|
144
154
|
```
|
|
145
155
|
|
|
146
156
|
**Performance Evidence**:
|
|
157
|
+
|
|
147
158
|
```
|
|
148
159
|
Benchmark Results (1000 simple queries):
|
|
149
160
|
- DBAPI: 2.3 seconds
|
|
@@ -163,23 +174,27 @@ For admin ops → Use docker exec (when iris.connect() unavailable)
|
|
|
163
174
|
**The Principle**: Every test suite gets its own isolated IRIS instance unless explicitly shared.
|
|
164
175
|
|
|
165
176
|
**Why It Matters**:
|
|
177
|
+
|
|
166
178
|
- Shared databases cause test pollution
|
|
167
179
|
- Parallel test execution requires isolation
|
|
168
180
|
- Cleanup failures cascade to other tests
|
|
169
181
|
- "Works alone but fails in suite" mysteries
|
|
170
182
|
|
|
171
183
|
**Implementation Requirements**:
|
|
184
|
+
|
|
172
185
|
- ✅ Testcontainers for test isolation
|
|
173
186
|
- ✅ Unique namespaces per test class
|
|
174
187
|
- ✅ test_run_id tracking for data cleanup
|
|
175
188
|
- ✅ Automatic cleanup even on test failure
|
|
176
189
|
|
|
177
190
|
**Forbidden**:
|
|
191
|
+
|
|
178
192
|
- ❌ Shared databases without cleanup
|
|
179
193
|
- ❌ Assuming tests run sequentially
|
|
180
194
|
- ❌ Leaving test data behind
|
|
181
195
|
|
|
182
196
|
**Scope Guidelines**:
|
|
197
|
+
|
|
183
198
|
```python
|
|
184
199
|
# Module scope: Fast, shared state acceptable
|
|
185
200
|
@pytest.fixture(scope="module")
|
|
@@ -199,12 +214,14 @@ def iris_db_isolated():
|
|
|
199
214
|
**The Principle**: `pip install iris-devtester && pytest` must work without configuration.
|
|
200
215
|
|
|
201
216
|
**Why It Matters**:
|
|
217
|
+
|
|
202
218
|
- Reduces onboarding friction
|
|
203
219
|
- Enables quick experimentation
|
|
204
220
|
- Makes examples self-contained
|
|
205
221
|
- CI/CD "just works"
|
|
206
222
|
|
|
207
223
|
**Implementation Requirements**:
|
|
224
|
+
|
|
208
225
|
- ✅ Sensible defaults for all configuration
|
|
209
226
|
- ✅ Auto-discovery of IRIS instances
|
|
210
227
|
- ✅ Community edition defaults
|
|
@@ -212,11 +229,13 @@ def iris_db_isolated():
|
|
|
212
229
|
- ✅ Explicit configuration always possible
|
|
213
230
|
|
|
214
231
|
**Forbidden**:
|
|
232
|
+
|
|
215
233
|
- ❌ Required configuration files
|
|
216
234
|
- ❌ Mandatory environment variables
|
|
217
235
|
- ❌ Undocumented prerequisites
|
|
218
236
|
|
|
219
237
|
**Configuration Hierarchy** (highest priority first):
|
|
238
|
+
|
|
220
239
|
1. Explicit constructor arguments
|
|
221
240
|
2. Environment variables
|
|
222
241
|
3. .env files in project root
|
|
@@ -228,11 +247,13 @@ def iris_db_isolated():
|
|
|
228
247
|
**The Principle**: Errors must be detected immediately with clear remediation steps.
|
|
229
248
|
|
|
230
249
|
**Why It Matters**:
|
|
250
|
+
|
|
231
251
|
- Debugging time is expensive
|
|
232
252
|
- Stack traces without context are useless
|
|
233
253
|
- Developers need actionable guidance
|
|
234
254
|
|
|
235
255
|
**Implementation Requirements**:
|
|
256
|
+
|
|
236
257
|
- ✅ Detect errors at initialization, not first use
|
|
237
258
|
- ✅ Include "What went wrong" explanation
|
|
238
259
|
- ✅ Include "How to fix it" remediation
|
|
@@ -240,11 +261,13 @@ def iris_db_isolated():
|
|
|
240
261
|
- ✅ Link to relevant documentation
|
|
241
262
|
|
|
242
263
|
**Forbidden**:
|
|
264
|
+
|
|
243
265
|
- ❌ Generic error messages
|
|
244
266
|
- ❌ Stack traces without explanation
|
|
245
267
|
- ❌ "Contact administrator" without details
|
|
246
268
|
|
|
247
269
|
**Example**:
|
|
270
|
+
|
|
248
271
|
```python
|
|
249
272
|
# WRONG
|
|
250
273
|
raise ConnectionError("Failed to connect")
|
|
@@ -275,12 +298,14 @@ raise ConnectionError(
|
|
|
275
298
|
**The Principle**: Support both Community and Enterprise editions equally well.
|
|
276
299
|
|
|
277
300
|
**Why It Matters**:
|
|
301
|
+
|
|
278
302
|
- Different projects have different needs
|
|
279
303
|
- Development often uses Community, production uses Enterprise
|
|
280
304
|
- License management is complex
|
|
281
305
|
- Mirror configurations are enterprise-only
|
|
282
306
|
|
|
283
307
|
**Implementation Requirements**:
|
|
308
|
+
|
|
284
309
|
- ✅ Community edition as default
|
|
285
310
|
- ✅ Enterprise edition via license_key parameter
|
|
286
311
|
- ✅ Auto-discovery of license files
|
|
@@ -288,28 +313,51 @@ raise ConnectionError(
|
|
|
288
313
|
- ✅ Clear documentation of edition differences
|
|
289
314
|
|
|
290
315
|
**Forbidden**:
|
|
316
|
+
|
|
291
317
|
- ❌ Hardcoded edition assumptions
|
|
292
318
|
- ❌ Enterprise-only code paths without community fallback
|
|
293
319
|
- ❌ Obscure license errors
|
|
294
320
|
|
|
295
321
|
**License Discovery Order**:
|
|
322
|
+
|
|
296
323
|
1. Explicit `license_key` parameter
|
|
297
324
|
2. `IRIS_LICENSE_KEY` environment variable
|
|
298
325
|
3. `~/.iris/iris.key`
|
|
299
326
|
4. `./iris.key` in project root
|
|
300
327
|
5. Auto-discovered from Docker volume mounts
|
|
301
328
|
|
|
329
|
+
**`_SYSTEM` Password Assumption — Read Before Using Enterprise Images**:
|
|
330
|
+
|
|
331
|
+
IDT's default credential (`_SYSTEM`/`SYS`) reflects the factory default shipped in InterSystems community images. Enterprise and HealthShare images often ship with:
|
|
332
|
+
|
|
333
|
+
- Expired `_SYSTEM` password requiring immediate change at first login
|
|
334
|
+
- Different default password or no default password at all
|
|
335
|
+
- `ChangePassword=1` flag set, blocking programmatic login
|
|
336
|
+
|
|
337
|
+
`attach()` calls `unexpire_all_passwords()` by default (wildcard unexpire via
|
|
338
|
+
`UnExpireUserPasswords("*")`) to handle this silently. For `start()`-managed
|
|
339
|
+
containers the CPF merge sets `PasswordNeverExpires=1` for `SuperUser` and
|
|
340
|
+
`_SYSTEM` pre-boot, then `reset_password()` applies any custom password
|
|
341
|
+
post-start via `PasswordExternal` (accepts plaintext — **never** put plaintext
|
|
342
|
+
into `PasswordHash=` in a CPF file, which expects `hash,salt` PBKDF2 format).
|
|
343
|
+
|
|
344
|
+
If login fails against an enterprise image with "password expired" or "access
|
|
345
|
+
denied" even after attach, call `unexpire_all_passwords(container_name)` manually
|
|
346
|
+
or pass `unexpire_passwords=True` explicitly.
|
|
347
|
+
|
|
302
348
|
### 7. MEDICAL-GRADE RELIABILITY
|
|
303
349
|
|
|
304
350
|
**The Principle**: All code must be battle-tested in production scenarios with comprehensive error handling.
|
|
305
351
|
|
|
306
352
|
**Why It Matters**:
|
|
353
|
+
|
|
307
354
|
- Healthcare applications require 99.9%+ uptime
|
|
308
355
|
- Silent failures are unacceptable
|
|
309
356
|
- Diagnostic data saves hours of debugging
|
|
310
357
|
- Idempotency prevents cascading failures
|
|
311
358
|
|
|
312
359
|
**Implementation Requirements**:
|
|
360
|
+
|
|
313
361
|
- ✅ 95%+ test coverage
|
|
314
362
|
- ✅ All error paths tested
|
|
315
363
|
- ✅ Idempotent operations (safe to retry)
|
|
@@ -319,12 +367,14 @@ raise ConnectionError(
|
|
|
319
367
|
- ✅ Health check endpoints
|
|
320
368
|
|
|
321
369
|
**Forbidden**:
|
|
370
|
+
|
|
322
371
|
- ❌ Untested error paths
|
|
323
372
|
- ❌ Non-idempotent operations
|
|
324
373
|
- ❌ Silent failures
|
|
325
374
|
- ❌ Assumptions about state
|
|
326
375
|
|
|
327
376
|
**Reliability Checklist**:
|
|
377
|
+
|
|
328
378
|
```python
|
|
329
379
|
# Every operation must answer:
|
|
330
380
|
- [ ] What happens if this fails?
|
|
@@ -340,24 +390,28 @@ raise ConnectionError(
|
|
|
340
390
|
**The Principle**: MUST use the official `iris.connect()` interface from InterSystems documentation. NEVER use undocumented private attributes like `_DBAPI`.
|
|
341
391
|
|
|
342
392
|
**Why It Matters**:
|
|
393
|
+
|
|
343
394
|
- `_DBAPI` does NOT exist in intersystems-irispython (tested in v5.1.2 and v5.3.0)
|
|
344
395
|
- Using non-existent attributes causes mysterious import failures
|
|
345
396
|
- Official API is `iris.connect()` per InterSystems documentation
|
|
346
397
|
- Private attributes are subject to change without warning
|
|
347
398
|
|
|
348
399
|
**Implementation Requirements**:
|
|
400
|
+
|
|
349
401
|
- ✅ Use `iris.connect()` for DBAPI connections (official DB-API 2.0 interface)
|
|
350
|
-
- ✅ Follow official InterSystems documentation: https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=BPYNAT_pyapi
|
|
402
|
+
- ✅ Follow official InterSystems documentation: <https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=BPYNAT_pyapi>
|
|
351
403
|
- ✅ Never import from private modules (anything starting with `_`)
|
|
352
404
|
- ✅ Test compatibility with both v5.1.2 and v5.3.0
|
|
353
405
|
|
|
354
406
|
**Forbidden**:
|
|
355
|
-
|
|
407
|
+
|
|
408
|
+
- ❌ `from intersystems_iris.dbapi._DBAPI import connect` - **\_DBAPI does not exist!**
|
|
356
409
|
- ❌ `iris._DBAPI.connect()` - **No such attribute!**
|
|
357
410
|
- ❌ Any code relying on undocumented internal APIs
|
|
358
411
|
- ❌ Assuming package structure without verification
|
|
359
412
|
|
|
360
413
|
**Correct API Usage**:
|
|
414
|
+
|
|
361
415
|
```python
|
|
362
416
|
# ✅ CORRECT - Official DB-API 2.0 interface
|
|
363
417
|
import iris
|
|
@@ -378,6 +432,7 @@ iris._DBAPI.connect(...) # AttributeError!
|
|
|
378
432
|
```
|
|
379
433
|
|
|
380
434
|
**Empirical Evidence**:
|
|
435
|
+
|
|
381
436
|
```python
|
|
382
437
|
# Tested on intersystems-irispython v5.1.2 and v5.3.0
|
|
383
438
|
import iris
|
|
@@ -386,8 +441,9 @@ hasattr(iris, 'connect') # True in BOTH versions!
|
|
|
386
441
|
```
|
|
387
442
|
|
|
388
443
|
**Official Documentation**:
|
|
389
|
-
|
|
390
|
-
-
|
|
444
|
+
|
|
445
|
+
- InterSystems Python API: <https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=BPYNAT_pyapi>
|
|
446
|
+
- DB-API 2.0 Specification: <https://peps.python.org/pep-0249/>
|
|
391
447
|
|
|
392
448
|
**Reference**: See hipporag2-pipeline CONSTITUTION.md Principle 9 for detailed empirical testing results.
|
|
393
449
|
|
|
@@ -396,12 +452,14 @@ hasattr(iris, 'connect') # True in BOTH versions!
|
|
|
396
452
|
**The Principle**: IRIS setup and connectivity should be invisible. A developer should go from "nothing" to "connected" with zero manual configuration.
|
|
397
453
|
|
|
398
454
|
**Why It Matters**:
|
|
455
|
+
|
|
399
456
|
- SQLite is the benchmark for "zero-friction" development
|
|
400
457
|
- Infrastructure setup is a major barrier to IRIS adoption
|
|
401
458
|
- Developer "flow" is interrupted by infrastructure management
|
|
402
459
|
- If a user has to think about ports, passwords, or container lifecycles, we have more work to do.
|
|
403
460
|
|
|
404
461
|
**Implementation Requirements**:
|
|
462
|
+
|
|
405
463
|
- ✅ `get_connection()` with zero-config auto-discovery
|
|
406
464
|
- ✅ Automatic container lifecycle management (transparent to the user)
|
|
407
465
|
- ✅ Standardized default credentials (`_SYSTEM`/`SYS`)
|
|
@@ -409,11 +467,13 @@ hasattr(iris, 'connect') # True in BOTH versions!
|
|
|
409
467
|
- ✅ Silent infrastructure: infrastructure "noise" (logs, status) is hidden unless an error occurs
|
|
410
468
|
|
|
411
469
|
**Forbidden**:
|
|
470
|
+
|
|
412
471
|
- ❌ Requiring manual Docker commands for basic connectivity
|
|
413
472
|
- ❌ Mandatory configuration files for local dev
|
|
414
473
|
- ❌ Exposing infrastructure complexity in the primary API
|
|
415
474
|
|
|
416
475
|
**Example**:
|
|
476
|
+
|
|
417
477
|
```python
|
|
418
478
|
# THE NORTH STAR EXPERIENCE
|
|
419
479
|
from iris_devtester import IRISContainer
|
|
@@ -427,25 +487,28 @@ conn = IRISContainer.community().get_connection()
|
|
|
427
487
|
**The Principle**: Failed approaches must be documented to prevent repetition.
|
|
428
488
|
|
|
429
489
|
**Why It Matters**:
|
|
490
|
+
|
|
430
491
|
- Developers waste time rediscovering "why not"
|
|
431
492
|
- Institutional knowledge must be preserved
|
|
432
493
|
- Context for design decisions is valuable
|
|
433
494
|
- Prevents regression to worse solutions
|
|
434
495
|
|
|
435
496
|
**Documented Blind Alleys**:
|
|
497
|
+
|
|
436
498
|
- **Why not JDBC-only?** → DBAPI is 3x faster, see benchmark
|
|
437
499
|
- **Why not shared test databases?** → Data pollution, see case study
|
|
438
500
|
- **Why not manual password resets?** → CI/CD breaks, see incident report
|
|
439
501
|
- **Why not port 1972 hardcoded?** → Conflicts in parallel tests, see issue #42
|
|
440
502
|
- **Why not prefer `iris.connect()`?** → In `intersystems-irispython` 5.3.1+, `iris.connect` uses the `_elsdk_` driver which has a critical `SIGSEGV` bug with `FETCH FIRST N ROWS ONLY` syntax. Prefer `intersystems_iris.connect` (the stable `_IRISNative` path) instead.
|
|
441
503
|
|
|
442
|
-
|
|
443
504
|
**Example Documentation**:
|
|
505
|
+
|
|
444
506
|
```markdown
|
|
445
507
|
## Why Not Use Docker Compose for Tests?
|
|
446
508
|
|
|
447
509
|
**What we tried**: Using docker-compose.yml for test database
|
|
448
510
|
**Why it didn't work**:
|
|
511
|
+
|
|
449
512
|
- Parallel tests conflicted on ports
|
|
450
513
|
- Cleanup required manual intervention
|
|
451
514
|
- CI/CD required docker-compose installation
|
|
@@ -462,12 +525,14 @@ conn = IRISContainer.community().get_connection()
|
|
|
462
525
|
### Amendment Process
|
|
463
526
|
|
|
464
527
|
Principles may be amended when:
|
|
528
|
+
|
|
465
529
|
1. New evidence contradicts existing principle
|
|
466
530
|
2. Technology landscape changes materially
|
|
467
531
|
3. Production experience reveals gap
|
|
468
532
|
4. Community consensus emerges
|
|
469
533
|
|
|
470
534
|
**Amendment requires**:
|
|
535
|
+
|
|
471
536
|
- Concrete evidence (benchmarks, case studies, incident reports)
|
|
472
537
|
- Backwards compatibility analysis
|
|
473
538
|
- Migration guide for existing code
|
|
@@ -477,18 +542,21 @@ Principles may be amended when:
|
|
|
477
542
|
### Enforcement
|
|
478
543
|
|
|
479
544
|
**Pre-commit hooks** validate:
|
|
545
|
+
|
|
480
546
|
- [ ] No hardcoded passwords or credentials
|
|
481
547
|
- [ ] All database operations are idempotent
|
|
482
548
|
- [ ] Error messages include remediation
|
|
483
549
|
- [ ] Test isolation via testcontainers or unique namespaces
|
|
484
550
|
|
|
485
551
|
**CI/CD validates**:
|
|
552
|
+
|
|
486
553
|
- [ ] 95%+ test coverage
|
|
487
554
|
- [ ] All platforms (Linux, Mac, Windows)
|
|
488
555
|
- [ ] Both Community and Enterprise editions
|
|
489
556
|
- [ ] Performance benchmarks (no regressions)
|
|
490
557
|
|
|
491
558
|
**Code review checklist**:
|
|
559
|
+
|
|
492
560
|
- [ ] Follows DBAPI-first principle
|
|
493
561
|
- [ ] Automatic remediation implemented
|
|
494
562
|
- [ ] Comprehensive error handling
|
|
@@ -498,12 +566,14 @@ Principles may be amended when:
|
|
|
498
566
|
## Version History
|
|
499
567
|
|
|
500
568
|
### v1.2.0 (2026-02-06)
|
|
569
|
+
|
|
501
570
|
- Added Principle 9: SQLite-Level Ergonomics (The North Star)
|
|
502
571
|
- Renumbered Principle 9 "Document the Blind Alleys" → Principle 10
|
|
503
572
|
- Standardized on `_SYSTEM` as the default toolkit username
|
|
504
573
|
- Renamed project to `iris-devtester` consistently across documentation
|
|
505
574
|
|
|
506
575
|
### v1.1.0 (2025-11-23)
|
|
576
|
+
|
|
507
577
|
- Added Principle 8: Use Official IRIS Python API (No Private Attributes)
|
|
508
578
|
- Documents empirically tested fact that `_DBAPI` does NOT exist in intersystems-irispython v5.1.2 or v5.3.0
|
|
509
579
|
- Mandates use of official `iris.connect()` API per InterSystems documentation
|
|
@@ -511,6 +581,7 @@ Principles may be amended when:
|
|
|
511
581
|
- Based on critical findings from hipporag2-pipeline project testing
|
|
512
582
|
|
|
513
583
|
### v1.0.0 (2025-10-05)
|
|
584
|
+
|
|
514
585
|
- Initial constitution
|
|
515
586
|
- 8 core principles established
|
|
516
587
|
- Based on rag-templates production experience
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iris-devtester
|
|
3
|
-
Version: 1.19.
|
|
3
|
+
Version: 1.19.4
|
|
4
4
|
Summary: Battle-tested InterSystems IRIS infrastructure utilities for Python development
|
|
5
5
|
Author-email: InterSystems Community <community@intersystems.com>
|
|
6
6
|
Maintainer-email: Thomas Dyar <thomas.dyar@intersystems.com>
|
|
@@ -40,15 +40,18 @@ class TempCPFManager:
|
|
|
40
40
|
file_path = os.path.abspath(tmp.name)
|
|
41
41
|
self._temp_files.add(file_path)
|
|
42
42
|
|
|
43
|
-
# Register for automatic cleanup
|
|
44
|
-
|
|
43
|
+
# Register for automatic cleanup.
|
|
44
|
+
# Must use the unbound method — passing self._delete_file would hold
|
|
45
|
+
# a strong reference to self through the bound method, preventing GC.
|
|
46
|
+
weakref.finalize(self, TempCPFManager._delete_file, file_path)
|
|
45
47
|
|
|
46
48
|
logger.debug(f"Created temporary CPF file: {file_path}")
|
|
47
49
|
return file_path
|
|
48
50
|
finally:
|
|
49
51
|
tmp.close()
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
@staticmethod
|
|
54
|
+
def _delete_file(file_path: str):
|
|
52
55
|
"""Internal helper to safely delete a file."""
|
|
53
56
|
try:
|
|
54
57
|
if os.path.exists(file_path):
|
|
@@ -7,6 +7,7 @@ from iris_devtester.config import IRISConfig
|
|
|
7
7
|
from iris_devtester.connections import get_connection
|
|
8
8
|
from iris_devtester.containers.connection_info import IRISConnectionInfo
|
|
9
9
|
from iris_devtester.containers.models import ContainerHealth, ContainerHealthStatus, HealthCheckLevel, ValidationResult
|
|
10
|
+
from iris_devtester.utils.password import reset_password, unexpire_all_passwords
|
|
10
11
|
|
|
11
12
|
logger = logging.getLogger(__name__)
|
|
12
13
|
|
|
@@ -350,7 +351,13 @@ class IRISContainer(IRISBase):
|
|
|
350
351
|
return container
|
|
351
352
|
|
|
352
353
|
@classmethod
|
|
353
|
-
def attach(
|
|
354
|
+
def attach(
|
|
355
|
+
cls,
|
|
356
|
+
container_name: str,
|
|
357
|
+
port: Optional[int] = None,
|
|
358
|
+
unexpire_passwords: bool = True,
|
|
359
|
+
**kwargs,
|
|
360
|
+
) -> "IRISContainer":
|
|
354
361
|
"""
|
|
355
362
|
Attach to an existing IRIS container by name.
|
|
356
363
|
|
|
@@ -360,18 +367,47 @@ class IRISContainer(IRISBase):
|
|
|
360
367
|
|
|
361
368
|
Args:
|
|
362
369
|
container_name: Name of the existing Docker container.
|
|
370
|
+
port: Host-side SuperServer port to use instead of the Docker-inspected
|
|
371
|
+
binding. Required when Docker Desktop NAT rewrites the source IP and
|
|
372
|
+
IRIS rejects the connection (macOS Docker Desktop with irishealth
|
|
373
|
+
enterprise). When supplied, the docker port lookup is skipped entirely.
|
|
374
|
+
Also honoured via ``IVG_PORT`` or ``IRIS_PORT`` environment variables
|
|
375
|
+
when ``port`` is not provided explicitly.
|
|
376
|
+
unexpire_passwords: When True (default), call unexpire_all_passwords() after
|
|
377
|
+
attaching. Enterprise images often ship with expired passwords; this
|
|
378
|
+
prevents silent login failures. Set to False when the container is known
|
|
379
|
+
to have valid credentials and you want to skip the overhead.
|
|
363
380
|
**kwargs: Additional configuration (username, password, namespace).
|
|
364
381
|
|
|
365
382
|
Returns:
|
|
366
383
|
An IRISContainer instance attached to the existing container.
|
|
367
384
|
"""
|
|
385
|
+
import os
|
|
386
|
+
|
|
368
387
|
if not container_name:
|
|
369
388
|
raise ValueError("container_name must be a non-empty string")
|
|
370
389
|
|
|
390
|
+
# Resolve explicit port override: parameter > IVG_PORT > IRIS_PORT
|
|
391
|
+
if port is None:
|
|
392
|
+
for env_var in ("IVG_PORT", "IRIS_PORT"):
|
|
393
|
+
env_val = os.environ.get(env_var)
|
|
394
|
+
if env_val:
|
|
395
|
+
try:
|
|
396
|
+
port = int(env_val)
|
|
397
|
+
except ValueError:
|
|
398
|
+
pass
|
|
399
|
+
else:
|
|
400
|
+
break
|
|
401
|
+
|
|
371
402
|
instance = cls(image="", name=container_name, **kwargs)
|
|
372
403
|
instance._is_attached = True
|
|
373
404
|
instance._container_name = container_name
|
|
374
405
|
|
|
406
|
+
# Pin the mapped port immediately so get_mapped_port(1972) never falls
|
|
407
|
+
# through to the docker port lookup, regardless of which code path below runs.
|
|
408
|
+
if port is not None:
|
|
409
|
+
instance._mapped_port = port
|
|
410
|
+
|
|
375
411
|
try:
|
|
376
412
|
import docker
|
|
377
413
|
|
|
@@ -379,15 +415,16 @@ class IRISContainer(IRISBase):
|
|
|
379
415
|
container = client.containers.get(container_name)
|
|
380
416
|
instance._container = container
|
|
381
417
|
|
|
382
|
-
# If it's a mock container, we need to set these manually
|
|
383
418
|
if not HAS_TESTCONTAINERS:
|
|
384
419
|
instance.host = "localhost"
|
|
385
|
-
#
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
420
|
+
# Only read docker port binding when no explicit port was given
|
|
421
|
+
if port is None:
|
|
422
|
+
ports = container.attrs.get("NetworkSettings", {}).get("Ports", {})
|
|
423
|
+
if "1972/tcp" in ports and ports["1972/tcp"]:
|
|
424
|
+
instance._mapped_port = int(ports["1972/tcp"][0]["HostPort"])
|
|
389
425
|
else:
|
|
390
|
-
# testcontainers will handle discovery via get_config()
|
|
426
|
+
# testcontainers will handle host/port discovery via get_config(),
|
|
427
|
+
# but _mapped_port is already pinned above if port was supplied.
|
|
391
428
|
instance.get_config()
|
|
392
429
|
|
|
393
430
|
except Exception as e:
|
|
@@ -404,6 +441,15 @@ class IRISContainer(IRISBase):
|
|
|
404
441
|
" 3. Check if your user has permission to access the Docker socket"
|
|
405
442
|
)
|
|
406
443
|
|
|
444
|
+
if unexpire_passwords:
|
|
445
|
+
# Enterprise images often ship with expired passwords; wildcard unexpire
|
|
446
|
+
# prevents silent login failures. Failure is non-fatal — the container
|
|
447
|
+
# is already attached and the caller may supply valid credentials.
|
|
448
|
+
try:
|
|
449
|
+
unexpire_all_passwords(container_name)
|
|
450
|
+
except Exception:
|
|
451
|
+
pass
|
|
452
|
+
|
|
407
453
|
return instance
|
|
408
454
|
|
|
409
455
|
def with_name(self, name: str) -> "IRISContainer":
|
|
@@ -837,17 +883,14 @@ class IRISContainer(IRISBase):
|
|
|
837
883
|
from iris_devtester.config.presets import CPFPreset
|
|
838
884
|
|
|
839
885
|
if not hasattr(self, "_cpf_temp_files") or not self._cpf_temp_files:
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
else:
|
|
849
|
-
cpf = CPFPreset.SECURE_DEFAULTS
|
|
850
|
-
self.with_cpf_merge(cpf)
|
|
886
|
+
# Always use SECURE_DEFAULTS for the CPF merge — it handles CallIn
|
|
887
|
+
# and clears expiry flags for SuperUser/_SYSTEM via proper CPF fields.
|
|
888
|
+
# When a custom password is requested we reset it post-start via
|
|
889
|
+
# PasswordExternal (plaintext accepted) rather than injecting it into
|
|
890
|
+
# PasswordHash= here, which requires a pre-computed PBKDF2 hash,salt
|
|
891
|
+
# string — passing plaintext into PasswordHash= sets a corrupt hash
|
|
892
|
+
# and makes every subsequent login fail silently.
|
|
893
|
+
self.with_cpf_merge(CPFPreset.SECURE_DEFAULTS)
|
|
851
894
|
|
|
852
895
|
self._password_handled = True
|
|
853
896
|
|
|
@@ -866,6 +909,21 @@ class IRISContainer(IRISBase):
|
|
|
866
909
|
|
|
867
910
|
super().start()
|
|
868
911
|
self.get_config()
|
|
912
|
+
|
|
913
|
+
# Post-start: apply custom password via PasswordExternal (accepts plaintext).
|
|
914
|
+
# Must happen after super().start() so the container is ready.
|
|
915
|
+
if self._preconfigure_password:
|
|
916
|
+
config = self.get_config()
|
|
917
|
+
username = self._preconfigure_username or "SuperUser"
|
|
918
|
+
reset_password(
|
|
919
|
+
container_name=self.get_container_name(),
|
|
920
|
+
username=username,
|
|
921
|
+
new_password=self._preconfigure_password,
|
|
922
|
+
hostname=config.host,
|
|
923
|
+
port=config.port,
|
|
924
|
+
verify=False,
|
|
925
|
+
)
|
|
926
|
+
|
|
869
927
|
self._password_preconfigured = True
|
|
870
928
|
return self
|
|
871
929
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iris-devtester
|
|
3
|
-
Version: 1.19.
|
|
3
|
+
Version: 1.19.4
|
|
4
4
|
Summary: Battle-tested InterSystems IRIS infrastructure utilities for Python development
|
|
5
5
|
Author-email: InterSystems Community <community@intersystems.com>
|
|
6
6
|
Maintainer-email: Thomas Dyar <thomas.dyar@intersystems.com>
|