diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..9352860 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,130 @@ +# AGENTS.md — Standing Knowledge & Instructions + +## 1. Project Purpose & Scope +**Task-Manager** is a task and project management web application built with Python and Flask. It allows users to organize tasks under project tabs, manage open/closed task statuses, rename projects and tasks, and interact through either a web browser interface or a RESTful JSON API documented via Swagger. + +--- + +## 2. Verified Technology Stack +*(Source of truth: `pyproject.toml`, `task_manager/__init__.py`, `task_manager/app.py`)* + +* **Language**: Python (`>=3.13` as declared in `pyproject.toml`) +* **Web Framework**: Flask 3.1.3 +* **Database & ORM**: SQLite (`ctm.db`), Flask-SQLAlchemy 3.1.1, SQLAlchemy 2.0.41 +* **API Documentation**: Flasgger 0.9.7.1 (Swagger UI at `/apidocs/`) +* **Frontend**: HTML5, Jinja2 3.1.6, Bootstrap 3.3.7 (CDN), jQuery 1.12.4 (CDN), custom UX script (`ux.js`) +* **Test Suite**: `pytest` 9.0.3, `pytest-cov` 6.2.1 + +--- + +## 3. Project Entry Points & Running +* **Development Server**: `python task_manager/app.py` or `make dev` (Runs Flask dev server on `http://localhost:5000`) +* **Production Server**: `make prod` (Runs Gunicorn server on `http://localhost:8000`) +* **Interactive API Documentation**: `http://localhost:5000/apidocs/` +* **Application Factory**: `task_manager/__init__.py:create_app()` + +--- + +## 4. Test Execution & Quality Assurance +The project supports standard Python environment runners. When using `uv` in this sandbox environment: +* **Run All Tests**: `uv run pytest` +* **Run with Coverage**: `uv run pytest --cov=task_manager --cov-report=term-missing` +* **Code Formatting / Type Checks** (configured in `pyproject.toml`): + ```bash + uv run black --check task_manager tests + uv run isort --check task_manager tests + uv run mypy task_manager + ``` + +--- + +## 5. Architecture & Execution Flow + +``` + ┌─────────────────────────┐ + │ USER / HTTP CLIENT │ + └────────────┬────────────┘ + │ + ┌─────────────────┴─────────────────┐ + │ │ + ▼ ▼ + [ WEB UI REQUESTS ] [ REST API REQUESTS ] + e.g., GET /, POST /add e.g., GET /api/tasks, + GET /project/ POST /api/tasks, PUT /api/... + │ │ + └─────────────────┬─────────────────┘ + │ + ▼ + [ Flask Blueprint Routes ] + (task_manager/routes.py) + │ + ┌───────────────────────────┴───────────────────────────┐ + │ │ + ▼ ▼ +[ Jinja2 View Renderer ] [ JSON Response ] +(task_manager/templates/index.html) (Flask jsonify) + │ │ + └───────────────────────────┬───────────────────────────┘ + │ + ▼ + [ SQLAlchemy ORM Models ] + (task_manager/models.py) + ├── Projects (project_id, project_name, active, url_slug) + └── Tasks (task_id, project_id, task, status) + │ + ▼ + [ SQLite Database ] + (instance/ctm.db) +``` + +--- + +## 6. Repository Map + +``` +task-manager/ +├── task_manager/ +│ ├── __init__.py # Application factory (create_app) & DB initialization +│ ├── app.py # Main entrypoint with Flasgger Swagger initialization +│ ├── models.py # Database models (Projects, Tasks) +│ ├── routes.py # Web UI routes & REST API endpoints +│ ├── templates/ +│ │ └── index.html # Jinja2 template for web interface +│ └── static/ +│ └── js/ +│ └── ux.js # Client-side JavaScript interactions +├── tests/ +│ ├── conftest.py # Pytest fixtures (app, client, create_project, create_task) +│ ├── test_models.py # ORM Model unit tests +│ ├── test_routes.py # Web route integration tests +│ ├── test_api.py # Web API integration tests +│ └── test_rest.py # REST API unit tests +├── harness_learning/ # Exploration reports, implementation plans, and micro-contexts +├── pyproject.toml # Project metadata, dependencies, and tool settings +├── Makefile # Target commands (dev, prod, test, clean) +└── REST_README.md # REST API usage documentation +``` + +--- + +## 7. Development Conventions +1. **Preserve Existing Signatures**: Ensure function and model parameter lists retain backwards compatibility to prevent breaking callers and test fixtures. +2. **Explicit Form & JSON Fallbacks**: Route handlers processing `request.form` or `request.get_json()` must handle missing keys gracefully using fallbacks. +3. **ORM Model Methods**: Domain logic and slug generation belong directly on model classes in `models.py`. +4. **Clean Commits**: Commit messages should follow standard Git conventions (short subject line under 50 characters, blank line, clear body detailing reasoning). + +--- + +## 8. What NOT To Do (Negative Constraints) +* **DO NOT** modify codebase files during exploration phases when only research or planning is requested. +* **DO NOT** write code without progressive exploration and context gathering first. +* **DO NOT** introduce heavy external dependencies (e.g. migration frameworks or custom enum libraries) when simple Python standard types suffice. +* **DO NOT** state claims as verified truth without citing inspected files or test output. + +--- + +## 9. Verification Instructions +Before declaring any task complete, perform three levels of verification: +1. **Behavioral Verification**: Confirm that the implemented requirement actually works end-to-end as intended in runtime execution, not merely that automated tests exist. +2. **Regression & Test Verification**: Execute `uv run pytest` to verify that relevant unit and integration tests pass and no regressions were introduced. +3. **Repository State Verification**: Run `git status` to confirm that only intended files were modified or created. diff --git a/harness_learning/due_date_spec.md b/harness_learning/due_date_spec.md new file mode 100644 index 0000000..42ced59 --- /dev/null +++ b/harness_learning/due_date_spec.md @@ -0,0 +1,99 @@ +# Specification: Task Due Dates Feature + +## 1. Overview & Purpose +This specification defines the requirement for adding an **optional Due Date** field to tasks in the Task-Manager application. When specified, the due date is displayed directly in front of the task description in `DD/MM` format. If the due date has passed, the due date label turns **red**. Tasks created without a due date (and all pre-existing tasks in the database) default to showing `"Unassigned"`. + +--- + +## 2. Core Requirements & Scope Boundaries + +### Included Features: +1. **Optional Due Date Input**: User can optionally select or enter a due date during task creation via Web UI or REST API. +2. **Display Format**: Rendered in `DD/MM` format (e.g. `25/12`) directly in front of the status checkbox / task action icons. +3. **Unassigned Fallback**: Tasks without a due date display `"Unassigned"`. All existing records in the database inherit `"Unassigned"`. +4. **Overdue Highlighting**: If the current date exceeds the task's due date, the due date text/badge renders in **red** (e.g., Bootstrap CSS `text-danger` or `label-danger`). +5. **REST API & Swagger Integration**: `due_date` field exposed in GET, POST, and PUT API responses/payloads. + +### Explicit Negative Constraints (Out of Scope): +* **NO Reminders**: Do NOT build email, push, or popup reminder functionality. +* **NO Notifications**: Do NOT send background notifications or queue system messages. +* **NO Recurring Schedules**: Do NOT build recurring due date logic or calendar sync integrations. + +--- + +## 3. Interfaces & Data Contracts + +### A. Database Schema (`task_manager/models.py`) +* **New Column**: `due_date = db.Column(db.String(10), default="Unassigned")` +* **Model Constructor**: + ```python + def __init__(self, project_id, task, status=True, priority="Medium", due_date="Unassigned"): + self.project_id = project_id + self.task = task + self.status = status + self.priority = priority + self.due_date = due_date if due_date else "Unassigned" + ``` + +### B. Web Interface (`task_manager/templates/index.html` & `routes.py`) +1. **Creation Form**: + * Add optional date input field in task form: + `` +2. **Task Table Display**: + * Position: Rendered just before the status check icon / column in `index.html`. + * Template Logic: + ```jinja2 + {% if task.due_date and task.due_date != "Unassigned" and is_overdue(task.due_date) %} + {{ task.due_date }} + {% else %} + {{ task.due_date }} + {% endif %} + ``` + +### C. REST API Contracts (`task_manager/routes.py`) +1. **`GET /api/tasks` & `GET /api/tasks/`**: + * Response payload key: `"due_date": "25/12"` or `"due_date": "Unassigned"`. +2. **`POST /api/tasks`**: + * Optional JSON property: `"due_date": "25/12"`. If omitted or null, defaults to `"Unassigned"`. +3. **`PUT /api/tasks/`**: + * Optional JSON property: `"due_date": "25/12"`. Updates task due date if provided. + +--- + +## 4. Blast Radius Analysis & Risk Mitigation + +| Component | Blast Radius Risk | Mitigation Strategy | +| :--- | :--- | :--- | +| **`Tasks.__init__` Constructor** | **High**: Adding `due_date` as a required parameter breaks all existing model instantiations in routes and unit test fixtures. | Make `due_date="Unassigned"` an optional keyword argument with default value. | +| **Database Backward Compatibility** | **High**: Existing rows in `ctm.db` will have `NULL` or missing `due_date` values. | Python property getter / helper method treats `None` or missing value as `"Unassigned"`. | +| **Date Parsing Mismatch** | **Medium**: Users or API clients submitting dates in `YYYY-MM-DD` vs `DD/MM` formats. | Standardize formatting logic helper `format_due_date()` in Python to format valid dates to `DD/MM` and fall back safely to `"Unassigned"`. | +| **Overdue Comparison** | **Medium**: Date comparison logic failing across month boundaries or leap years. | Parse `DD/MM` with current year context using Python `datetime` for overdue comparison (`task_date < today`). | + +--- + +## 5. Verification & Test Strategy + +To verify feature correctness without regressions: + +1. **Model Tests (`tests/test_models.py`)**: + * Verify default `due_date` is `"Unassigned"`. + * Verify explicit `due_date` storing (e.g. `"15/08"`). +2. **Web Route & Overdue Styling Tests (`tests/test_routes.py`)**: + * Verify task creation with and without `due_date`. + * **Mandatory Test**: Verify that if a due date has passed (e.g., yesterday's date formatted as `DD/MM`), the rendered HTML contains the `text-danger` class or red styling indicator. +3. **REST API Tests (`tests/test_rest.py`)**: + * Verify `GET /api/tasks` returns `"due_date"` field for all records. + * Verify `POST /api/tasks` accepts `"due_date"`. + * Verify `PUT /api/tasks/` updates `"due_date"`. + +--- + +## 6. Acceptance Criteria (Definition of Done) + +* **AC 1**: Tasks created without a due date show `"Unassigned"`. +* **AC 2**: Pre-existing tasks in the database display `"Unassigned"`. +* **AC 3**: Due dates display in `DD/MM` format positioned before the status check icon. +* **AC 4**: When a task due date is in the past, it renders in **red** text/style on the UI. +* **AC 5**: REST API GET, POST, and PUT endpoints support `"due_date"`. +* **AC 6**: Automated test asserts that overdue tasks render with red styling indicator. +* **AC 7**: Zero reminders, notifications, or extraneous features are added. diff --git a/harness_learning/exploration_report.md b/harness_learning/exploration_report.md new file mode 100644 index 0000000..3f9b133 --- /dev/null +++ b/harness_learning/exploration_report.md @@ -0,0 +1,83 @@ +# Exploration Report: Task Priority Feature + +## 1. Boundary & Exploration Scope Constraint +* **Boundary Rule**: Source code (`task_manager/`, `tests/`) was strictly preserved without modification during exploration. +* **Artifact Scope**: This document in `harness_learning/` is generated specifically to capture exploration findings and structural dependencies as requested. + +--- + +## 2. Observed Repository Facts vs Inferences vs Proposed Assumptions + +### A. Observed Repository Facts (Grounded in Code Inspection) +1. **`task_manager/models.py`**: + * `Tasks` table schema currently has columns: `task_id` (Integer, Primary Key), `project_id` (Integer, ForeignKey), `task` (Text), `status` (Boolean, default False). + * `Tasks.__init__(self, project_id, task, status=True)` initializes these attributes. +2. **`task_manager/routes.py`**: + * `/add` (`POST`) retrieves `task = request.form.get("task")`, `project = request.form.get("project")`, `status = bool(int(request.form.get("status")))`. + * `/api/tasks` (`GET`) serializes tasks into dictionaries with keys `id`, `project_id`, `task`, `status`. + * `/api/tasks` (`POST`) accepts `project_id`, `task`, `status` from JSON payload. + * `/api/tasks/` (`PUT`) updates `task` and `status` from JSON payload. +3. **`conftest.py`**: + * `create_task` fixture calls `Tasks(project_id=project.project_id, task=task_desc, status=status)`. + +### B. Inferences (Derived Architectural Relationships) +1. Adding a new attribute to `Tasks` requires synchronized updates across ORM model attributes, form parsing, JSON serialization/deserialization, and test fixtures. +2. Form handlers use `request.form.get()` which defaults to `None` if a form parameter is missing; API endpoints use `request.get_json()` which requires dict key checking. + +### C. Proposed Implementation Assumptions (Future Design Choices) +1. Task priority will be stored as a string field (`"Low"`, `"Medium"`, `"High"`) with a default value of `"Medium"`. +2. All existing tasks and legacy API requests without explicit priority will automatically default to `"Medium"`. + +--- + +## 3. Structural Dependency Relationships vs Runtime Execution Flow + +### A. Structural Dependency Relationships +* **`task_manager/models.py:Tasks`** + └── Dependent on: `db.Model` (SQLAlchemy) +* **`task_manager/routes.py`** + ├── Dependent on: `task_manager/models.py:Tasks` + ├── Dependent on: `task_manager/models.py:Projects` + └── Dependent on: `task_manager/templates/index.html` +* **`tests/conftest.py`** + └── Dependent on: `task_manager/models.py:Tasks` +* **`tests/test_routes.py`, `tests/test_api.py`, `tests/test_rest.py`** + └── Dependent on: `conftest.py` fixtures and `routes.py` endpoints + +### B. Runtime Execution Flow for Task Operations + +``` +[ Web UI Form Submission ] [ REST API JSON Request ] + POST /add POST /api/tasks + │ │ + ▼ ▼ +Extract form fields Extract JSON fields + (task, project, status) (project_id, task, status) + │ │ + └───────────────────────┬────────────────────────┘ + │ + ▼ + Instantiate Tasks Model + Tasks(project_id, task, status) + │ + ▼ + SQLAlchemy DB Session Commit + db.session.add() & db.session.commit() + │ + ┌───────────────────────┴────────────────────────┐ + │ │ + ▼ ▼ +Redirect to / (Render index.html) Return 201 Created JSON Payload +``` + +--- + +## 4. Grounding Verification Matrix + +| Claim in Exploration Report | Inspected File & Line Evidence | Verification Status | +| :--- | :--- | :--- | +| `Tasks` model has `task_id`, `project_id`, `task`, `status` | `task_manager/models.py` (lines 28-34) | **Grounded** | +| `/add` route handles task creation via form data | `task_manager/routes.py` (lines 43-85) | **Grounded** | +| `/api/tasks` GET endpoint serializes task fields | `task_manager/routes.py` (lines 191-213) | **Grounded** | +| `create_task` test fixture instantiates `Tasks` | `conftest.py` (lines 38-46) | **Grounded** | +| Working tree is clean and 55 pytest tests pass | Terminal command `uv run pytest` output | **Grounded** | diff --git a/harness_learning/implementation_plan.md b/harness_learning/implementation_plan.md new file mode 100644 index 0000000..f3ff872 --- /dev/null +++ b/harness_learning/implementation_plan.md @@ -0,0 +1,72 @@ +# Implementation Plan: Task Priority Feature + +## 1. Discovered Repository Facts vs Proposed Design Choices + +### A. Discovered Repository Facts +1. Tasks currently possess `task_id`, `project_id`, `task`, and `status`. +2. Task creation occurs in two routes: `/add` (Web HTML form) and `/api/tasks` (REST JSON API). +3. The test suite uses a shared fixture `create_task` in `conftest.py` which passes positional/keyword arguments to `Tasks(...)`. +4. The database is initialized via `db.create_all()` in `task_manager/__init__.py`. + +### B. Proposed Design Choices (Task Priority Feature) +1. **Priority Values**: Represented as strings: `"Low"`, `"Medium"`, `"High"`. +2. **Default Priority**: Set to `"Medium"` across models, web forms, and REST endpoints. +3. **Database Representation**: `db.Column(db.String(10), default="Medium")`. + +--- + +## 2. Multi-Dimensional Backward Compatibility Analysis + +To guarantee that adding priority introduces zero regressions across existing code and tests: + +1. **Model Constructor Compatibility**: + * *Strategy*: `Tasks.__init__(self, project_id, task, status=True, priority="Medium")` + * *Impact*: Existing calls like `Tasks(project_id, task)` or `Tasks(project_id, task, status)` continue working without argument count mismatches. +2. **Test Fixture Compatibility**: + * *Strategy*: If `Tasks.__init__` has a default parameter `priority="Medium"`, existing test fixtures like `create_task` in `conftest.py` automatically work without requiring mandatory changes. +3. **API Payload Compatibility**: + * *Strategy*: `GET /api/tasks` includes `"priority"` as an additive key. `POST /api/tasks` and `PUT /api/tasks/` use `.get("priority", "Medium")` and `.get("priority", task.priority)` respectively. + * *Impact*: Clients omitting `"priority"` in JSON payloads continue functioning seamlessly. +4. **Database & Schema Compatibility**: + * *Strategy*: New SQLite instances created via `db.create_all()` include the `priority` column automatically. For existing development databases, falling back gracefully or recreating the test DB handles the change cleanly. +5. **Form Input & Rendering Compatibility**: + * *Strategy*: `request.form.get("priority", "Medium")` safely defaults if an old form submission lacks the input. The Jinja2 template checks for `task.priority` or displays default styling. + +--- + +## 3. Justification of Minimum Change Against Alternatives + +| Proposed Approach | Alternative 1: Integer Priority Levels (1, 2, 3) | Alternative 2: Custom DB Enum / PostgreSQL Enum | Alternative 3: Separate Priority Table | Alternative 4: Migration Framework (Alembic) | +| :--- | :--- | :--- | :--- | :--- | +| **Choice**: String (`"Low"`, `"Medium"`, `"High"`) | **Rejected**: Requires mapping integers to human-readable labels in UI and API docs. | **Rejected**: SQLite does not natively support SQL ENUM types; requires dialect-specific workarounds. | **Rejected**: Creates unnecessary join queries (`JOIN priority`) for a simple 3-level attribute. Overengineers data model. | **Rejected**: Project uses `db.create_all()` without Alembic migration infrastructure. Introducing Alembic adds unnecessary complexity. | +| **Justification**: Storing strings (`String(10)`) is human-readable, natively supported by SQLite/SQLAlchemy, and requires zero extra dependencies. | + +--- + +## 4. Behavior-Oriented Acceptance Criteria (Definition of Done) + +The implementation will be considered complete **ONLY** when the following externally observable behaviors are satisfied: + +* **AC 1 (Default Priority Assignment)**: + When a task is created without specifying priority (via Web UI or REST API), the system assigns it `"Medium"` priority. +* **AC 2 (Explicit Priority Creation via Web UI)**: + When a user selects `"High"` or `"Low"` priority in the web form and submits, the created task reflects that priority in the task list. +* **AC 3 (Explicit Priority Creation via REST API)**: + When a JSON payload containing `{"priority": "High"}` is POSTed to `/api/tasks`, the returned 201 response payload contains `"priority": "High"`. +* **AC 4 (Priority Updates via REST API)**: + When a JSON payload containing `{"priority": "Low"}` is PUT to `/api/tasks/`, subsequent GET requests to `/api/tasks/` return `"priority": "Low"`. +* **AC 5 (REST API Payload Contract)**: + All task objects returned by `GET /api/tasks` and `GET /api/tasks/` contain the `"priority"` key. +* **AC 6 (Zero Regression Guarantee)**: + All 55 pre-existing unit tests pass without failure or modification of existing test logic. + +--- + +## 5. Implementation Task List (Separated from Acceptance Criteria) +*(To be executed only when feature development begins)* + +1. Add `priority` column to `Tasks` in `task_manager/models.py`. +2. Update `/add` form handling in `task_manager/routes.py`. +3. Update REST endpoints (`/api/tasks`) in `task_manager/routes.py`. +4. Update web UI template `task_manager/templates/index.html`. +5. Add unit tests for priority behavior in `tests/`. diff --git a/harness_learning/micro_context.md b/harness_learning/micro_context.md new file mode 100644 index 0000000..8aae87e --- /dev/null +++ b/harness_learning/micro_context.md @@ -0,0 +1,56 @@ +# Task Priority Feature: Micro Context & Guardrails + +## 1. Role Delineation of Harness Artifacts +* **`AGENTS.md`**: Standing project knowledge, environment conventions, and permanent agent directives. +* **`harness_learning/exploration_report.md`**: Grounded repository evidence, structural dependencies, and execution flow. +* **`harness_learning/implementation_plan.md`**: Task intent, minimal design choices, and behavioral acceptance criteria. +* **`harness_learning/micro_context.md`**: Targeted code symbols, inspection vs modification boundaries, and quantitative debugging protocols. + +--- + +## 2. Symbol-Level Target Mapping + +| Target Symbol / Function | File Location | Purpose & Action | +| :--- | :--- | :--- | +| `class Tasks(db.Model)` | `task_manager/models.py` | Add `priority` Column definition and update `__init__` constructor signature with default `priority="Medium"`. | +| `def add_task()` | `task_manager/routes.py` | Extract `request.form.get("priority", "Medium")` and pass to `Tasks`. | +| `def api_get_tasks()` & `api_get_task()` | `task_manager/routes.py` | Add `"priority": t.priority` to JSON serialization dict. | +| `def api_create_task()` | `task_manager/routes.py` | Extract `data.get("priority", "Medium")` from JSON body when creating task. | +| `def api_update_task()` | `task_manager/routes.py` | Update `task.priority = data.get("priority", task.priority)` from JSON body. | +| Form & Table HTML snippets | `task_manager/templates/index.html` | Add ` + Priority +
  @@ -148,6 +154,7 @@

  DESCRIPTION + PRIORITY STATUS @@ -174,6 +181,11 @@

  {% endif %} + + + {{ task.priority }} + + {% if task.status %} diff --git a/tests/test_models.py b/tests/test_models.py index 25e8fdd..cf33924 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -8,3 +8,35 @@ def test_task_model_repr(app, create_task): task = create_task(task_desc="Test Task") with app.app_context(): assert repr(task) == f"" + + +def test_task_model_priority_default(app, create_task): + task = create_task(task_desc="Default Priority Task") + with app.app_context(): + assert task.priority == "Medium" + + +def test_task_model_priority_explicit(app, create_project): + from task_manager import db + from task_manager.models import Tasks + + project = create_project(name="Priority Project") + task = Tasks(project_id=project.project_id, task="Urgent Task", status=True, priority="High") + db.session.add(task) + db.session.commit() + + with app.app_context(): + assert task.priority == "High" + + +def test_task_model_priority_invalid_fallback(app, create_project): + from task_manager import db + from task_manager.models import Tasks + + project = create_project(name="Invalid Priority Project") + task = Tasks(project_id=project.project_id, task="Invalid Task", status=True, priority="Urgent") + db.session.add(task) + db.session.commit() + + with app.app_context(): + assert task.priority == "Medium" diff --git a/tests/test_rest.py b/tests/test_rest.py index bbb1238..660ccc6 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -52,6 +52,7 @@ def test_get_tasks(client, create_task): assert len(data) == 1 assert data[0]["id"] == task.task_id assert data[0]["task"] == "Task1" + assert data[0]["priority"] == "Medium" def test_get_task_valid(client, create_task): @@ -61,6 +62,7 @@ def test_get_task_valid(client, create_task): assert response.status_code == 200 assert data["id"] == task.task_id assert data["task"] == "Task1" + assert data["priority"] == "Medium" def test_get_task_invalid(client): @@ -86,13 +88,23 @@ def test_create_project_fail(client): def test_create_task_success(client, create_project): project = create_project("P", True) - payload = {"project_id": project.project_id, "task": "New Task", "status": False} + payload = { + "project_id": project.project_id, + "task": "New Task", + "status": False, + "priority": "High", + } response = client.post("/api/tasks", json=payload) data = json.loads(response.data) assert response.status_code == 201 assert "id" in data assert data["message"] == "Task created" + created_id = data["id"] + get_res = client.get(f"/api/tasks/{created_id}") + task_data = json.loads(get_res.data) + assert task_data["priority"] == "High" + def test_create_task_fail(client): response = client.post("/api/tasks", json={"task": "No project"}) @@ -124,7 +136,7 @@ def test_update_project_fail(client): def test_update_task_success(client, create_task, app): task = create_task("OldTask", True) - payload = {"task": "NewTask", "status": False} + payload = {"task": "NewTask", "status": False, "priority": "Low"} response = client.put(f"/api/tasks/{task.task_id}", json=payload) data = json.loads(response.data) assert response.status_code == 200 @@ -134,6 +146,7 @@ def test_update_task_success(client, create_task, app): updated = db.session.get(Tasks, task.task_id) assert updated.task == "NewTask" assert updated.status is False + assert updated.priority == "Low" def test_update_task_fail(client): diff --git a/tests/test_routes.py b/tests/test_routes.py index 9455fca..d3e784e 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -111,6 +111,24 @@ def test_add_task_existing_project_used(client, app): assert task.task == "Task X" +def test_add_task_with_explicit_priority(client, app): + response = client.post( + "/add", + data={ + "task": "High Priority Task", + "project": "Work", + "status": "1", + "priority": "High", + }, + ) + assert response.status_code == 302 + + with app.app_context(): + task = Tasks.query.filter_by(task="High Priority Task").first() + assert task is not None + assert task.priority == "High" + + # Test /close