diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5226c14 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/_bmad +/_bmad* +/.agents +/.github \ No newline at end of file diff --git a/README.md b/README.md index 728f45a..279f9f6 100644 --- a/README.md +++ b/README.md @@ -76,3 +76,13 @@ Move generated file to `app/controllers/`. - Classic ASP enabled - IIS URL Rewrite module - Microsoft Access Database Engine (for .accdb support) + +## Testing + +This repo now includes a dev-only `aspunit` harness under `tests/`. It is intentionally separate from the production app rooted at `public/`. + +- Configure a separate IIS application rooted at `tests/` +- Ensure Classic ASP parent paths are enabled for that IIS app +- If you change `tests/web.config`, refresh the nested test-folder copies with `cscript //nologo tests\sync-webconfigs.vbs` +- Open `run-all.asp` inside that IIS app to execute the test suite +- See `TESTING.md` for setup, manifest registration, and extension guidance diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..5107fed --- /dev/null +++ b/TESTING.md @@ -0,0 +1,121 @@ +# Testing MVC-Starter + +This project now supports a dev-only Classic ASP test harness built with `aspunit`. + +## Scope + +- Production app: `public/` +- Dev-only test app: `tests/` +- Test framework: `tests/aspunit/` + +The `tests/` IIS application assumes the repository layout keeps `tests/`, `public/`, `core/`, and `app/` as sibling directories. + +## File Inventory + +| Type | Path | Purpose | +| ---- | ---- | ------- | +| Create | `tests/web.config` | IIS/default-document config for the isolated test app | +| Create | `tests/unit/web.config` | Mirrored config for nested unit pages that load config-aware code | +| Create | `tests/component/web.config` | Mirrored config for nested component pages that load config-aware code | +| Create | `tests/integration/web.config` | Mirrored config for nested integration pages that load config-aware code | +| Create | `tests/sync-webconfigs.vbs` | Utility script to mirror `tests/web.config` into nested test folders | +| Create | `tests/bootstrap.asp` | Shared test bootstrap and runtime reset helpers | +| Create | `tests/PlainRunnerTheme.asp` | Local runner theme that removes CDN dependence from the test UI | +| Create | `tests/test-manifest.asp` | Single source of truth for registered test pages | +| Create | `tests/run-all.asp` | Browser runner that aggregates all test pages | +| Vendor | `tests/aspunit/Lib/*` | Upstream aspunit framework files | +| Vendor | `tests/aspunit/LICENSE-MIT` | Upstream aspunit license for vendored third-party code | +| Create | `tests/unit/TestHelpers.asp` | Deterministic helper-function unit tests | +| Create | `tests/unit/TestControllerRegistry.asp` | Controller whitelist/format unit tests | +| Create | `tests/component/TestHomeController.asp` | Controlled component-level controller test | +| Create | `tests/integration/TestMvcDispatch.asp` | Narrow router/dispatch smoke test | +| Reference | `public/web.config` | Source of mirrored config keys for the test app | +| Reference | `core/helpers.asp` | Helper functions and config-loading behavior under test | +| Reference | `core/mvc.asp` | Dispatcher behavior used by the smoke test | +| Reference | `core/lib.ControllerRegistry.asp` | Whitelist behavior under test | +| Verify | `public/` site | Confirm production app exposes no test routes/pages | + +## IIS Setup + +1. Keep the existing production IIS app rooted at `public/`. +2. Create a separate development-only IIS application rooted at `tests/`. +3. Enable Classic ASP for that IIS app. +4. Ensure parent paths are allowed for the `tests/` app. This repo ships `tests/web.config` with `enableParentPaths="true"` because the bootstrap and integration pages include sibling files from `../core/` and `../app/`. +5. Browse to the `tests/` app root or directly to `run-all.asp`. +6. If you change `tests/web.config`, run `cscript //nologo tests\sync-webconfigs.vbs` to refresh the nested copies used by the unit, component, and integration pages. + +Example layout: + +- Production: `http://localhost/` +- Tests: `http://localhost/tests-dev/` + +## How It Works + +- `tests/run-all.asp` includes the aspunit library and the manifest. +- `tests/run-all.asp` also applies `PlainRunnerTheme.asp`, a local runner theme that avoids the upstream CDN dependency in aspunit’s default UI. +- `tests/test-manifest.asp` explicitly registers each test page. +- `tests/bootstrap.asp` provides the shared runtime setup: + - helper/config access + - controller registry access + - router reset + +The integration test page includes `core/mvc.asp` directly because that is the only first-wave test that needs dispatcher behavior. + +Because `GetAppSetting()` uses `Server.MapPath("web.config")`, nested test folders also need a mirrored `web.config` alongside the executing test pages that rely on config-aware runtime files. Use `tests/sync-webconfigs.vbs` after changing `tests/web.config`. + +The manifest is manual by design. There is no filesystem auto-discovery. + +## Running Tests + +Open the browser runner: + +Browse to `run-all.asp` within the `tests/` IIS application, for example: + +```text +http://localhost/tests-dev/run-all.asp +``` + +aspunit renders a UI in runner mode and loads each registered page with `?task=test` behind the scenes. + +## Adding a New Test Page + +1. Choose the right folder: + - `tests/unit/` for deterministic helper or registry tests + - `tests/component/` for direct controller/object tests with controlled setup + - `tests/integration/` for narrow runtime smoke coverage +2. Create a new `.asp` file that: + - includes `../aspunit/Lib/ASPUnit.asp` + - includes `../bootstrap.asp` + - registers one or more modules with `ASPUnit.AddModule(...)` + - calls `ASPUnit.Run()` +3. Add the page path to `tests/test-manifest.asp`. +4. Reload `run-all.asp`. + +## First-Pass Isolation Policy + +- Unit tests should avoid mutating globals. +- Registry tests should inspect current behavior, not expand the production whitelist. +- Component tests should reset touched singleton/controller state in setup/teardown. +- Dispatch smoke tests should initialize fresh route state before each run. + +## What Not To Unit Test + +- Full rendered page markup for shared-layout pages unless you add reliable output-capture support. +- Production IIS rewrite behavior from `public/`. +- Broad end-to-end request flows through the production site root. +- Config-dependent behavior that requires machine-specific production values unless you first mirror safe test values into `tests/web.config`. +- Internet access as a requirement for the runner UI. The local theme removes the upstream CDN dependency for first-wave execution. + +## Validation Checklist + +- `run-all.asp` loads in the separate `tests/` IIS app. +- All manifest pages appear in the runner. +- Helper, registry, component, and integration suites all execute. +- Re-running the suite produces stable results. +- The production site under `public/` still exposes no test runner pages or test routes. + +## Limitations + +- This harness runs only inside IIS/Classic ASP; it is not intended for Linux execution. +- The repo’s production runtime is still configured via `public/web.config`; the test app uses a minimal mirrored config in `tests/web.config`. +- If the narrow bootstrap is not sufficient for future smoke tests, broaden only the test bootstrap first before considering production runtime changes. diff --git a/docs/api-contracts-mvc-starter.md b/docs/api-contracts-mvc-starter.md new file mode 100644 index 0000000..655f436 --- /dev/null +++ b/docs/api-contracts-mvc-starter.md @@ -0,0 +1,63 @@ +# API Contracts - MVC-Starter + +**Date:** 2026-03-11T11:59:39Z + +## Overview + +This project does not expose a standalone JSON API. Its current externally reachable contracts are server-rendered HTTP routes handled by the Classic ASP MVC dispatcher. + +## Route Catalog + +### `GET /` + +- **Controller:** `homeController` +- **Action:** `Index` +- **Behavior:** Renders the home page using the shared layout and `app/views/Home/index.asp`. +- **Layout:** enabled (`useLayout = True`) +- **Response Type:** HTML +- **Status:** `200 OK` + +### `GET /home` + +- **Controller:** `homeController` +- **Action:** `Index` +- **Behavior:** Alias route to the home page; renders the same content as `/`. +- **Layout:** enabled +- **Response Type:** HTML +- **Status:** `200 OK` + +### `GET ""` (empty path fallback) + +- **Controller:** `homeController` +- **Action:** `Index` +- **Behavior:** Additional root-path fallback route declared in `public/Default.asp`. +- **Layout:** enabled +- **Response Type:** HTML +- **Status:** `200 OK` + +### `GET /404` + +- **Controller:** `ErrorController` +- **Action:** `NotFound` +- **Behavior:** Renders the not-found page and sets the response status to `404 Not Found`. +- **Layout:** enabled +- **Response Type:** HTML +- **Status:** `404 Not Found` + +## Dispatch Contract + +- All non-static requests are rewritten by IIS to `public/Default.asp`. +- `public/Default.asp` registers routes and calls `MVC.DispatchRequest`. +- `core/mvc.asp` validates controller and action names, checks the controller whitelist, resolves the controller dynamically, and invokes the action. +- If `useLayout` is enabled on the resolved controller, shared header and footer views wrap the response. + +## Security and Validation Notes + +- Controller names must pass format validation and whitelist checks in `core/lib.ControllerRegistry.asp`. +- Action names must pass identifier validation before dispatch. +- Current route handling is page-oriented; there are no documented JSON payload schemas or standalone API auth flows in the application itself. + +## Brownfield Notes + +- Future endpoint additions require updates in at least three places: route registration, controller include/implementation, and controller whitelist registration. +- This document reflects route contracts, not REST-style service endpoints. diff --git a/docs/architecture-patterns.md b/docs/architecture-patterns.md new file mode 100644 index 0000000..bc5d275 --- /dev/null +++ b/docs/architecture-patterns.md @@ -0,0 +1,34 @@ +# Architecture Patterns + +**Date:** 2026-03-11T11:59:39Z + +## Part: MVC-Starter + +### Primary Architecture Pattern + +Server-rendered MVC monolith with a starter/framework hybrid structure: IIS and `public/` handle request entry, `core/` provides shared runtime and dispatch behavior, and `app/` provides project-specific extensions. + +## Pattern Breakdown + +- **End-to-end request path:** IIS URL Rewrite sends requests to `public/Default.asp`, routes are registered there, the router resolves controller/action/params, `core/mvc.asp` validates and dispatches, and shared layout files wrap output when `useLayout` is enabled. +- **Routing pattern:** Routes are declared centrally in `public/Default.asp` and resolved through the RouteKit router object. +- **Dispatch pattern:** `core/mvc.asp` uses validation, controller whitelisting, and dynamic execution to resolve the current controller and action. +- **Application layering:** `core/` contains reusable framework/runtime behavior, while `app/` contains project-specific controllers, views, models, and repositories. +- **Rendering pattern:** Controllers orchestrate and include `.asp` view files; shared header/footer layout files provide common page chrome. +- **Data pattern:** Data access is config-driven through `public/web.config`, with DAL libraries and generator-assisted repositories/POBOs supporting database interaction. +- **Operational tooling pattern:** Schema and scaffolding workflows are handled by standalone VBScript tools under `scripts/`. + +## Architectural Implications + +- This codebase is optimized around a single IIS-hosted deployable unit rather than separable services. +- Framework bootstrapping and request dispatch are centralized, so edits to `public/` entry files or `core/` runtime files have broad impact. +- Dynamic dispatch is part of the framework design, which makes naming, registration, and validation rules part of the architecture contract. +- Feature development typically extends existing seams in `app/`, `db/`, and `scripts/` rather than introducing new application subsystems. +- The architecture assumes server-side HTML rendering and config-driven runtime behavior rather than client-heavy SPA patterns. + +## Brownfield Notes + +- The most important architectural boundary is between the framework/runtime layer (`core/`) and the app extension layer (`app/`). +- Controller activation is not automatic; routing, include registration, and whitelist registration are all part of the runtime contract. +- Windows/IIS hosting is part of the architecture, not just deployment detail, because request flow and configuration depend on it. +- Generator scripts are part of the implementation model, not just convenience tooling. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..ad5a6b6 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,81 @@ +# MVC-Starter - Architecture + +**Date:** 2026-03-11T11:59:39Z +**Project Type:** web +**Architecture:** Server-rendered MVC monolith + +## Executive Summary + +MVC-Starter is a Classic ASP starter application built around a single IIS-hosted deployable unit. Requests enter through `public/Default.asp`, flow through a shared runtime in `core/`, and resolve into application-specific controllers and views under `app/`. + +## Technology Stack + +- Classic ASP / VBScript +- Windows IIS with URL Rewrite +- RouteKit-style router and MVC dispatcher +- Microsoft Access via ACE OLE DB +- Bootstrap 5.3.3 and Bootstrap Icons 1.11.3 from CDN +- VBScript tooling for scaffolding and migrations + +## Architecture Pattern + +Starter/framework hybrid MVC monolith: + +- `public/` handles web entry and IIS-facing config +- `core/` handles routing, dispatch, validation, helpers, and DAL concerns +- `app/` contains project-specific behavior and server-rendered views +- `db/` and `scripts/` support data and operational workflows + +## Request Flow + +1. IIS receives the request. +2. `public/web.config` rewrites non-static requests to `public/Default.asp`. +3. `Default.asp` registers routes and calls `MVC.DispatchRequest`. +4. `core/mvc.asp` validates controller/action names, checks whitelist registration, resolves the current controller dynamically, and executes the target action. +5. Shared layout files wrap the response if `useLayout` is enabled. + +## Data Architecture + +- Database configuration is read from `public/web.config`. +- `db/webdata.accdb` is the included Access database. +- DAL and migration helpers live in `core/`. +- Repositories and POBOs are expected to be generated via `scripts/GenerateRepo.vbs`. + +## Component Overview + +- Controllers: `app/controllers/` +- Views: `app/views/` +- Shared layout: `app/views/shared/` +- Runtime/framework: `core/` +- Scaffolding/tooling: `scripts/` + +## Source Tree + +See [source-tree-analysis.md](./source-tree-analysis.md) for the annotated directory breakdown. + +## Development Workflow + +- Generate migration +- Generate POBO/repository +- Generate controller +- Register controller include, whitelist, and routes +- Add views and validate via IIS-hosted execution + +## Deployment Architecture + +- Single IIS site +- `public/` as web root +- XML config in `public/web.config` +- No separate service/process topology detected + +## Testing Strategy + +- No automated test framework detected +- Manual request-flow verification is required +- Script tooling can be validated via `cscript` + +## Architectural Constraints + +- Dynamic dispatch means naming and registration consistency are part of runtime correctness. +- `core/`, `public/Default.asp`, and `public/web.config` are high-impact files. +- The architecture assumes Windows/IIS hosting rather than cross-platform portability. diff --git a/docs/component-inventory.md b/docs/component-inventory.md new file mode 100644 index 0000000..cc5e19c --- /dev/null +++ b/docs/component-inventory.md @@ -0,0 +1,63 @@ +# MVC-Starter - Component Inventory + +**Date:** 2026-03-11T11:59:39Z + +## Runtime and MVC Components + +### Front Controller + +- `public/Default.asp` + - Registers routes + - Boots runtime + - Dispatches requests + +### Dispatcher + +- `core/mvc.asp` + - Validates controller/action names + - Enforces controller whitelist + - Wraps responses in shared layout when enabled + +### Controller Registry + +- `core/lib.ControllerRegistry.asp` + - Tracks valid controllers + - Prevents arbitrary controller execution + +### Controllers + +- `app/controllers/HomeController.asp` + - Home page controller +- `app/controllers/ErrorController.asp` + - 404/not-found handling + +## UI/View Components + +### Shared Layout + +- `app/views/shared/header.asp` +- `app/views/shared/footer.asp` + +### Feature Views + +- `app/views/Home/index.asp` +- `app/views/Error/NotFound.asp` + +## Data/Operational Components + +### Database Assets + +- `db/webdata.accdb` + +### Script Tooling + +- `scripts/generateController.vbs` +- `scripts/GenerateRepo.vbs` +- `scripts/generateMigration.vbs` +- `scripts/runMigrations.vbs` + +## Reuse Notes + +- The main reusable UI surface is the shared layout. +- The main reusable runtime surface is the `core/` library set. +- The data layer is scaffold-oriented and currently has no committed application-specific model/repository classes. diff --git a/docs/comprehensive-analysis-mvc-starter.md b/docs/comprehensive-analysis-mvc-starter.md new file mode 100644 index 0000000..c3ce8cd --- /dev/null +++ b/docs/comprehensive-analysis-mvc-starter.md @@ -0,0 +1,46 @@ +# Comprehensive Analysis - MVC-Starter + +**Date:** 2026-03-11T11:59:39Z + +## Configuration Management + +- Primary runtime configuration lives in `public/web.config`. +- Important settings include database connection, environment mode, flash timing, 404 redirect timing, cache controls, and error logging paths. +- Configuration is read in application code through helper accessors such as `GetAppSetting`. + +## Authentication and Security + +- No full authentication subsystem is present in the starter. +- The main observable security boundary is controller/action validation and controller whitelisting in `core/lib.ControllerRegistry.asp`. +- Dispatch also HTML-encodes some user-visible error output to reduce injection risk. + +## Entry Points and Bootstrap + +- IIS web root: `public/` +- Default document: `public/Default.asp` +- Runtime bootstrap include: `core/autoload_core.asp` +- Request dispatcher: `core/mvc.asp` + +## Shared Code + +- `core/` contains reusable framework/runtime libraries for routing, data access, flash messaging, forms, uploads, HTML helpers, validation, encryption helpers, and JSON support. +- `scripts/` contains operational generators and migration tooling. + +## Event/Async Patterns + +- No queue, worker, or event-bus architecture was detected. +- UI behavior is mostly server-rendered with small inline browser-side scripts where needed. + +## CI/CD and Deployment + +- No CI/CD pipeline configuration was detected. +- Deployment is IIS-based and documented in `README.md` and `public/web.config`. + +## Localization + +- No localization or i18n framework was detected. + +## Testing Surface + +- No automated test framework or test files were detected in the application code. +- Validation currently depends on manual runtime checks and script execution in a Windows/IIS environment. diff --git a/docs/critical-folders-summary.md b/docs/critical-folders-summary.md new file mode 100644 index 0000000..f53b4d5 --- /dev/null +++ b/docs/critical-folders-summary.md @@ -0,0 +1,11 @@ +# Critical Folders Summary + +**Date:** 2026-03-11T11:59:39Z + +- `public/`: externally facing web root, rewrite config, and request bootstrap +- `core/`: framework/runtime internals, dispatcher, router, helpers, DAL, and security checks +- `app/controllers/`: project controller implementations and include-based activation surface +- `app/views/`: server-rendered feature views and shared layout +- `db/`: Access database and migration workspace +- `scripts/`: generator and migration tooling used during feature development +- `docs/`: generated brownfield documentation for future planning and AI-assisted work diff --git a/docs/data-models-mvc-starter.md b/docs/data-models-mvc-starter.md new file mode 100644 index 0000000..408e980 --- /dev/null +++ b/docs/data-models-mvc-starter.md @@ -0,0 +1,42 @@ +# Data Models - MVC-Starter + +**Date:** 2026-03-11T11:59:39Z + +## Overview + +The repository includes a database file and data-access infrastructure, but it does not yet contain checked-in application model or repository source files under `app/models/` or `app/repositories/`. + +## Current Data Assets + +### Database File + +- **Path:** `db/webdata.accdb` +- **Type:** Microsoft Access database +- **Configured Provider:** `Microsoft.ACE.OLEDB.12.0` +- **Configured From:** `public/web.config` + +### Data Access Runtime + +- **DAL libraries:** `core/lib.DAL.asp`, `core/lib.Data.asp` +- **Migration support:** `core/lib.Migrations.asp` +- **Repository/model generation:** `scripts/GenerateRepo.vbs` + +## Schema and Model Strategy + +- The intended model pattern is generator-assisted. +- `GenerateRepo.vbs` inspects a target table and produces: + - `POBO_.asp` + - `
Repository.asp` +- Generated outputs are expected to be moved into `app/models/` and `app/repositories/`. + +## Current Repository State + +- `app/models/` is currently empty. +- `app/repositories/` is currently empty. +- `db/migrations/` currently has no checked-in migration files. + +## Brownfield Implications + +- The project is database-capable, but the sample starter does not include domain entities yet. +- Schema evolution and repository creation are expected to happen through the provided VBScript tooling rather than a separate ORM or package-managed migration framework. +- Future data documentation should be regenerated once concrete tables, migration files, or generated POBO/repository files are added. diff --git a/docs/deployment-configuration.md b/docs/deployment-configuration.md new file mode 100644 index 0000000..fc291fc --- /dev/null +++ b/docs/deployment-configuration.md @@ -0,0 +1,35 @@ +# Deployment Configuration + +**Date:** 2026-03-11T11:59:39Z + +## Hosting Model + +- Windows IIS +- `public/` configured as the site root +- `public/Default.asp` as the default document +- URL Rewrite sends non-static requests through the ASP front controller + +## Runtime Configuration + +Primary deployment configuration is stored in `public/web.config`. + +### Important Settings + +- `ConnectionString` +- `Environment` +- `FlashMessageTimeout` +- `Error404RedirectSeconds` +- `CacheExpirationYear` +- `EnableErrorLogging` +- `ErrorLogPath` + +## Deployment Notes + +- The Access DB path must be updated for the target machine. +- `ErrorLogPath` should be writable by the IIS application identity if enabled. +- Static assets are expected under `public/` paths excluded from rewrite rules. + +## Observed Gaps + +- No container, CI/CD, or infrastructure-as-code deployment config was detected. +- Deployment is currently documented as a manual IIS-based process. diff --git a/docs/deployment-guide.md b/docs/deployment-guide.md new file mode 100644 index 0000000..951813d --- /dev/null +++ b/docs/deployment-guide.md @@ -0,0 +1,37 @@ +# MVC-Starter - Deployment Guide + +**Date:** 2026-03-11T11:59:39Z + +## Deployment Model + +Single-site Windows IIS deployment with `public/` as the web root. + +## Deployment Steps + +1. Copy the repository to the target Windows host. +2. Configure the IIS site to point to `public/`. +3. Ensure Classic ASP is enabled. +4. Ensure URL Rewrite is installed. +5. Update `public/web.config` for the target environment. +6. Ensure the Access DB file path is valid and accessible. + +## Key Runtime Config + +- `ConnectionString` +- `Environment` +- `EnableErrorLogging` +- `ErrorLogPath` +- cache and UI timing settings + +## Deployment Risks + +- Incorrect `ConnectionString` path for `.accdb` +- Missing IIS URL Rewrite module +- Missing Classic ASP support +- File permission issues for logs or database access + +## What Was Not Found + +- No Docker, Kubernetes, or container deployment setup +- No CI/CD pipeline config +- No infrastructure-as-code deployment definition diff --git a/docs/development-guide.md b/docs/development-guide.md new file mode 100644 index 0000000..2e519fd --- /dev/null +++ b/docs/development-guide.md @@ -0,0 +1,55 @@ +# MVC-Starter - Development Guide + +**Date:** 2026-03-11T11:59:39Z + +## Prerequisites + +- Windows environment +- IIS with Classic ASP enabled +- IIS URL Rewrite module +- Microsoft Access Database Engine +- Windows Script Host (`cscript`) + +## Local Setup + +1. Place the project on a Windows machine with IIS. +2. Point the site root to `public/`. +3. Update `public/web.config` for the correct database and logging paths. +4. Browse to `/` and confirm the starter home page renders. + +## Common Workflows + +### Add a database-backed feature + +1. Generate a migration +2. Generate POBO/repository +3. Move generated files into `app/models/` and `app/repositories/` +4. Generate a controller +5. Include the controller from `app/controllers/autoload_controllers.asp` +6. Register the controller in `core/lib.ControllerRegistry.asp` +7. Add routes in `public/Default.asp` +8. Add corresponding views in `app/views/` + +### Useful Commands + +```bat +cscript //nologo scripts\generateMigration.vbs create_my_table +cscript //nologo scripts\GenerateRepo.vbs /table:my_table /pk:id +cscript //nologo scripts\generateController.vbs MyController "Index;Show(id)" +cscript //nologo scripts\runMigrations.vbs status +``` + +## Testing Approach + +- Manual runtime validation through IIS +- Manual route/layout verification after controller or view changes +- Script validation through `cscript` +- No built-in automated test suite detected + +## High-Risk Change Areas + +- `public/Default.asp` +- `public/web.config` +- `core/mvc.asp` +- `core/lib.ControllerRegistry.asp` +- shared layout files under `app/views/shared/` diff --git a/docs/development-instructions.md b/docs/development-instructions.md new file mode 100644 index 0000000..042163c --- /dev/null +++ b/docs/development-instructions.md @@ -0,0 +1,59 @@ +# Development Instructions + +**Date:** 2026-03-11T11:59:39Z + +## Prerequisites + +- Windows Server or Windows development environment +- IIS with Classic ASP enabled +- IIS URL Rewrite module +- Microsoft Access Database Engine for `.accdb` support +- Windows Script Host / `cscript` + +## Setup + +1. Copy the project to the target IIS host. +2. Point the IIS site root to `public/`. +3. Update `public/web.config`: + - `ConnectionString` + - `ErrorLogPath` if logging is desired +4. Ensure the Access database file path is valid for the environment. + +## Common Development Tasks + +### Generate a migration + +```bat +cscript //nologo scripts\generateMigration.vbs create_my_table +``` + +### Generate POBO and repository + +```bat +cscript //nologo scripts\GenerateRepo.vbs /table:my_table /pk:id +``` + +Move generated files into `app/models/` and `app/repositories/`. + +### Generate a controller + +```bat +cscript //nologo scripts\generateController.vbs MyController "Index;Show(id);Create;Store" +``` + +Move the generated controller into `app/controllers/`. + +### Activate a controller + +After generating a controller: + +1. Include it from `app/controllers/autoload_controllers.asp` +2. Register it in `core/lib.ControllerRegistry.asp` +3. Add routes in `public/Default.asp` +4. Create corresponding views in `app/views/` + +## Validation Approach + +- Manual browser validation is required for runtime changes. +- Route/layout changes should be checked through IIS-hosted execution. +- Migration and generator changes should be validated with `cscript`. diff --git a/docs/existing-documentation-inventory.md b/docs/existing-documentation-inventory.md new file mode 100644 index 0000000..934832c --- /dev/null +++ b/docs/existing-documentation-inventory.md @@ -0,0 +1,46 @@ +# Existing Documentation Inventory + +**Date:** 2026-03-11T11:59:39Z + +## Summary + +The project has a small set of directly relevant documentation files plus BMAD framework support files. For brownfield understanding, the highest-priority sources are the project README and a small number of repo-specific instruction files. + +## Priority Documentation for Project Understanding + +### `README.md` + +- **Type:** readme +- **Path:** `/workspace/MVC-Starter/README.md` +- **Scope:** whole project +- **Notes:** Documents IIS setup, project structure, generator workflow, and baseline runtime requirements. + +### `.github/copilot-instructions.md` + +- **Type:** project instructions +- **Path:** `/workspace/MVC-Starter/.github/copilot-instructions.md` +- **Scope:** whole project +- **Notes:** Documents BMAD runtime conventions, config locations, workflow engine usage, and agent/runtime structure. + +### `_bmad-output/project-context.md` + +- **Type:** generated ai-project-context +- **Path:** `/workspace/MVC-Starter/_bmad-output/project-context.md` +- **Scope:** whole project +- **Notes:** Derivative documentation generated from workflow analysis. Useful for AI implementation rules, but not an original source of project design intent. + +## BMAD Framework Documentation Assets + +- `.github/agents/*.md` and `.github/prompts/*.md` provide BMAD agent and prompt definitions. +- `_bmad/` contains the installed BMAD framework, workflows, templates, and configuration. +- These files are important for maintaining the BMAD layer, but they are secondary to the application docs when documenting the MVC starter itself. + +## Generated Documentation in This Workflow Run + +- Files created under `docs/` by this workflow should be treated as generated brownfield documentation outputs, not original project-authored documents. + +## Observations + +- No dedicated architecture, deployment, API, or data-model markdown docs existed for the application before this workflow run. +- The README is the main human-authored source of project usage and structure. +- Most other markdown assets in the repo belong to the BMAD toolchain rather than the Classic ASP application itself. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..550e377 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,71 @@ +# MVC-Starter Documentation Index + +**Type:** monolith +**Primary Language:** VBScript / Classic ASP +**Architecture:** Server-rendered MVC monolith +**Last Updated:** 2026-03-11T11:59:39Z + +## Project Overview + +MVC-Starter is a RouteKit Classic ASP starter application for Windows IIS. It uses a single front controller, shared runtime/framework libraries, server-rendered views, an Access database, and VBScript-based scaffolding tools. + +## Quick Reference + +- **Tech Stack:** Classic ASP, VBScript, IIS, Access, Bootstrap +- **Entry Point:** `public/Default.asp` +- **Architecture Pattern:** Starter/framework hybrid MVC monolith +- **Database:** `db/webdata.accdb` via ACE OLE DB +- **Deployment:** Manual IIS deployment + +## Generated Documentation + +### Core Documentation + +- [Project Overview](./project-overview.md) - Executive summary and high-level architecture +- [Project Structure](./project-structure.md) - Classification and structural boundaries +- [Source Tree Analysis](./source-tree-analysis.md) - Annotated directory structure +- [Architecture](./architecture.md) - Detailed technical architecture +- [Architecture Patterns](./architecture-patterns.md) - Runtime and design patterns +- [Technology Stack](./technology-stack.md) - Stack inventory and justification +- [Component Inventory](./component-inventory.md) - Runtime, UI, and tooling components +- [Development Guide](./development-guide.md) - Setup and common workflows +- [Deployment Guide](./deployment-guide.md) - IIS deployment and config guidance +- [API Contracts](./api-contracts-mvc-starter.md) - HTTP route contracts +- [Data Models](./data-models-mvc-starter.md) - Current data/model state and strategy + +### Supporting Analysis + +- [Existing Documentation Inventory](./existing-documentation-inventory.md) +- [User-Provided Context](./user-provided-context.md) +- [State Management Patterns](./state-management-patterns-mvc-starter.md) +- [UI Component Inventory](./ui-component-inventory-mvc-starter.md) +- [Comprehensive Analysis](./comprehensive-analysis-mvc-starter.md) +- [Critical Folders Summary](./critical-folders-summary.md) +- [Development Instructions](./development-instructions.md) +- [Deployment Configuration](./deployment-configuration.md) +- [Project Parts Metadata](./project-parts.json) +- [Project Scan State](./project-scan-report.json) + +## Existing Documentation + +- [README.md](../README.md) - Setup, structure, and feature scaffolding workflow +- [.github/copilot-instructions.md](../.github/copilot-instructions.md) - BMAD/Copilot repo instructions +- [_bmad-output/project-context.md](../_bmad-output/project-context.md) - Generated AI implementation context + +## Getting Started + +### For Humans + +1. Read [project-overview.md](./project-overview.md) +2. Review [architecture.md](./architecture.md) +3. Use [development-guide.md](./development-guide.md) for feature work + +### For AI-Assisted Development + +- Start from this index +- Use [architecture.md](./architecture.md) and [project-context.md](../_bmad-output/project-context.md) together +- Reference [api-contracts-mvc-starter.md](./api-contracts-mvc-starter.md) and [data-models-mvc-starter.md](./data-models-mvc-starter.md) for route/data planning + +--- + +Documentation generated by BMAD `document-project`. diff --git a/docs/project-overview.md b/docs/project-overview.md new file mode 100644 index 0000000..e306799 --- /dev/null +++ b/docs/project-overview.md @@ -0,0 +1,70 @@ +# MVC-Starter - Project Overview + +**Date:** 2026-03-11T11:59:39Z +**Type:** web +**Architecture:** Server-rendered MVC monolith + +## Executive Summary + +MVC-Starter is a RouteKit Classic ASP starter for building server-rendered MVC applications on Windows IIS. It combines a shared framework/runtime layer in `core/` with application-specific controllers and views in `app/`, and supports database-backed features through Access and VBScript-based scaffolding. + +## Project Classification + +- **Repository Type:** monolith +- **Project Type(s):** web +- **Primary Language(s):** VBScript, Classic ASP +- **Architecture Pattern:** Starter/framework hybrid MVC monolith + +## Technology Stack Summary + +- Runtime: Classic ASP on IIS +- Routing/dispatch: RouteKit-style router and MVC dispatcher +- Data: ADO + Microsoft Access +- UI: Bootstrap CDN + server-rendered ASP views +- Tooling: Windows Script Host VBScript scripts + +## Key Features + +- Front-controller request flow through `public/Default.asp` +- Controller whitelist and dynamic dispatch in `core/mvc.asp` +- Shared header/footer layout for page rendering +- Generator-assisted migrations, repositories, and controllers +- Included starter pages for home and 404 handling + +## Architecture Highlights + +- Single deployable web app +- Clear split between runtime/framework code and app-specific code +- Config-driven behavior through `public/web.config` +- Manual but structured development workflow through scripts and route/controller wiring + +## Development Overview + +### Prerequisites + +- Windows + IIS + Classic ASP +- IIS URL Rewrite +- Microsoft Access Database Engine +- `cscript` for tooling + +### Getting Started + +Point IIS at `public/`, update `public/web.config`, then validate the home page loads. Use the provided VBScript generators to create migrations, repositories, and controllers for new features. + +### Key Commands + +- **Generate migration:** `cscript //nologo scripts\generateMigration.vbs create_my_table` +- **Generate repo:** `cscript //nologo scripts\GenerateRepo.vbs /table:my_table /pk:id` +- **Generate controller:** `cscript //nologo scripts\generateController.vbs MyController "Index;Show(id)"` +- **Run migrations:** `cscript //nologo scripts\runMigrations.vbs status` + +## Repository Structure + +`public/` is the web entry surface, `core/` is the framework/runtime layer, `app/` is the application layer, `db/` contains database assets, and `scripts/` contains operational tooling. + +## Documentation Map + +- [index.md](./index.md) +- [architecture.md](./architecture.md) +- [source-tree-analysis.md](./source-tree-analysis.md) +- [development-guide.md](./development-guide.md) diff --git a/docs/project-parts.json b/docs/project-parts.json new file mode 100644 index 0000000..c68ffc7 --- /dev/null +++ b/docs/project-parts.json @@ -0,0 +1,26 @@ +{ + "repository_type": "monolith", + "parts": [ + { + "part_id": "mvc-starter", + "part_name": "MVC-Starter", + "root_path": "/workspace/MVC-Starter", + "project_type_id": "web", + "runtime_shape": "server-rendered-mvc-monolith", + "hosting_assumption": "Windows IIS with URL Rewrite", + "entry_point": "public/Default.asp", + "framework_runtime_path": "core/", + "application_surface_path": "app/", + "operational_support_paths": [ + "db/", + "scripts/" + ], + "primary_technologies": [ + "Classic ASP", + "VBScript", + "IIS", + "Microsoft Access" + ] + } + ] +} diff --git a/docs/project-scan-report.json b/docs/project-scan-report.json new file mode 100644 index 0000000..368fbe0 --- /dev/null +++ b/docs/project-scan-report.json @@ -0,0 +1 @@ +{"workflow_version":"1.2.0","timestamps":{"started":"2026-03-11T11:59:39Z","last_updated":"2026-03-11T12:13:30Z","completed":"2026-03-11T12:13:30Z"},"mode":"initial_scan","scan_level":"exhaustive","project_root":"/workspace/MVC-Starter","project_knowledge":"/workspace/MVC-Starter/docs","completed_steps":[{"step":"step_1","status":"completed","timestamp":"2026-03-11T12:03:40Z","summary":"Classified as monolith with 1 part; detected Classic ASP MVC web application hosted via IIS entrypoint in public/Default.asp"},{"step":"step_2","status":"completed","timestamp":"2026-03-11T12:06:06Z","summary":"Inventoried 3 relevant existing documentation files and recorded no additional user focus areas"},{"step":"step_3","status":"completed","timestamp":"2026-03-11T12:09:06Z","summary":"Analyzed technology stack and architecture pattern as Classic ASP/VBScript MVC monolith on IIS with Access and VBScript tooling"},{"step":"step_4","status":"completed","timestamp":"2026-03-11T12:12:40Z","summary":"Completed exhaustive conditional analysis and generated API, data, state, UI, and comprehensive analysis documents"},{"step":"step_5","status":"completed","timestamp":"2026-03-11T12:12:55Z","summary":"Generated source tree analysis and critical folders summary"},{"step":"step_6","status":"completed","timestamp":"2026-03-11T12:13:05Z","summary":"Generated development instructions and deployment configuration documentation"},{"step":"step_8","status":"completed","timestamp":"2026-03-11T12:13:10Z","summary":"Generated architecture.md for the single application part"},{"step":"step_9","status":"completed","timestamp":"2026-03-11T12:13:15Z","summary":"Generated supporting overview, component, development, deployment, and index-adjacent docs"},{"step":"step_10","status":"completed","timestamp":"2026-03-11T12:13:20Z","summary":"Generated master index with complete links and no incomplete markers"},{"step":"step_11","status":"completed","timestamp":"2026-03-11T12:13:25Z","summary":"Validated generated docs, found no incomplete documentation markers"},{"step":"step_12","status":"completed","timestamp":"2026-03-11T12:13:30Z","summary":"Workflow complete"}],"current_step":"completed","findings":{"project_classification":"monolith, 1 part, Classic ASP/VBScript web application","existing_documentation":"3 relevant docs found; README, Copilot instructions, and generated project context","technology_stack":"Classic ASP/VBScript on IIS with Access, RouteKit-style MVC runtime, Bootstrap CDN, and VBScript tooling","batch_summaries":["Application routes, controllers, views, and shared layout documented from app/ and public/","Runtime and framework behavior documented from core/","Database and operational tooling documented from db/ and scripts/"]},"project_types":[{"part_id":"mvc-starter","project_type_id":"web","display_name":"web"}],"outputs_generated":["project-scan-report.json","project-structure.md","project-parts.json","existing-documentation-inventory.md","user-provided-context.md","technology-stack.md","architecture-patterns.md","api-contracts-mvc-starter.md","data-models-mvc-starter.md","state-management-patterns-mvc-starter.md","ui-component-inventory-mvc-starter.md","comprehensive-analysis-mvc-starter.md","source-tree-analysis.md","critical-folders-summary.md","development-instructions.md","deployment-configuration.md","architecture.md","project-overview.md","component-inventory.md","development-guide.md","deployment-guide.md","index.md"],"resume_instructions":"Workflow complete. Start a fresh scan only if the project changes materially.","verification_summary":"Exhaustive file-system scan of app/core/public/scripts/db; extracted runtime config, routes, controllers, views, and tooling docs; checked docs for incomplete markers; no IIS runtime tests executed.","open_risks":"Documentation is based on static code/config analysis in this Linux workspace; Windows IIS runtime behavior, Access connectivity, and script execution were not validated live.","next_checks":"Review docs/index.md, validate the app on a Windows IIS host, confirm web.config paths, and regenerate docs after adding real models/repositories/migrations."} diff --git a/docs/project-structure.md b/docs/project-structure.md new file mode 100644 index 0000000..93b69fd --- /dev/null +++ b/docs/project-structure.md @@ -0,0 +1,38 @@ +# MVC-Starter Project Structure + +**Date:** 2026-03-11T11:59:39Z +**Repository Type:** monolith +**Detected Project Type:** web +**Specific Runtime Shape:** Classic ASP server-rendered MVC starter on Windows IIS +**Primary Technologies:** Classic ASP, VBScript, IIS, Microsoft Access + +## Classification Summary + +This repository is a single deployable web application, not a multi-part client/server system. It is a server-rendered Classic ASP MVC starter with: + +- `public/` as the IIS web root and deploy entrypoint +- `core/` as shared framework/runtime code +- `app/` as the application extension surface for controllers, views, models, and repositories +- `db/` as database and migration support +- `scripts/` as scaffolding and operational tooling + +## Detected Part + +### MVC-Starter + +- **Root Path:** `/workspace/MVC-Starter` +- **Project Type:** web +- **Runtime Shape:** Server-rendered MVC monolith +- **Hosting Assumption:** Windows IIS with URL Rewrite +- **Deploy Entry Point:** `public/Default.asp` +- **Framework Runtime:** `core/` +- **Application Extension Surface:** `app/` +- **Operational Support Directories:** `db/`, `scripts/` + +## Why This Classification Fits + +- The repository exposes a single web entrypoint through `public/Default.asp` and IIS rewrite config in `public/web.config`. +- Request handling flows through `public/` into framework code in `core/`, while application behavior is implemented in `app/`. +- Request handling, view rendering, routing, data access, and framework runtime are all contained in one application tree. +- There are no separate client/server packages, independent deployables, or service boundaries that would justify multi-part classification. +- The repo structure matches a classic server-rendered MVC application rather than a frontend/backend split web stack. diff --git a/docs/source-tree-analysis.md b/docs/source-tree-analysis.md new file mode 100644 index 0000000..806d0bb --- /dev/null +++ b/docs/source-tree-analysis.md @@ -0,0 +1,100 @@ +# MVC-Starter - Source Tree Analysis + +**Date:** 2026-03-11T11:59:39Z + +## Overview + +The repository is organized as a Classic ASP MVC starter with a clear split between deploy entry, shared runtime, application code, database assets, and operational tooling. + +## Complete Directory Structure + +```text +MVC-Starter/ +├── app/ # Application-specific MVC layer +│ ├── controllers/ # Controllers and controller includes +│ ├── models/ # Intended POBO/model location (currently empty) +│ ├── repositories/ # Intended repository location (currently empty) +│ └── views/ # Server-rendered ASP views +│ ├── Error/ +│ ├── Home/ +│ └── shared/ +├── core/ # Framework/runtime libraries and dispatcher +├── db/ # Database file and future migrations +│ ├── migrations/ +│ └── webdata.accdb +├── docs/ # Generated brownfield documentation +├── public/ # IIS web root and request entrypoint +│ ├── Default.asp +│ └── web.config +├── scripts/ # Scaffolding and migration tooling +├── _bmad/ # Installed BMAD framework assets +├── _bmad-output/ # Generated BMAD workflow artifacts +└── .github/ # Copilot/BMAD prompt and agent support files +``` + +## Critical Directories + +### `public/` + +Purpose: deploy-facing web root and request bootstrap. + +- Contains the front controller `Default.asp` +- Contains `web.config` with rewrite and runtime config +- Entry point for all non-static requests + +### `core/` + +Purpose: shared framework/runtime layer. + +- Contains dispatcher, router, controller registry, helpers, DAL, migrations, flash helpers, and utility libraries +- High-blast-radius area for architectural changes + +### `app/` + +Purpose: project-specific extension surface. + +- `controllers/` contains active controllers and include registration +- `views/` contains page templates and shared layout files +- `models/` and `repositories/` are intended generator targets + +### `db/` + +Purpose: database storage and schema evolution support. + +- Includes `webdata.accdb` +- Includes `migrations/` directory for migration files + +### `scripts/` + +Purpose: development and operations support tooling. + +- Controller generator +- Repository/POBO generator +- Migration generator +- Migration runner + +## Entry Points + +- Main request entry: `public/Default.asp` +- Runtime bootstrap: `core/autoload_core.asp` +- MVC dispatcher: `core/mvc.asp` +- Migration tooling entry: `scripts/runMigrations.vbs` + +## Configuration Files + +- `public/web.config` - IIS routing and runtime application settings +- `README.md` - setup and workflow guidance +- `.github/copilot-instructions.md` - BMAD/Copilot workflow guidance for repo maintenance + +## File Organization Patterns + +- Request handling starts in `public/`, not `app/` +- Shared reusable runtime code lives in `core/` +- Feature-specific behavior belongs in `app/` +- Operational scripts live outside the runtime in `scripts/` +- Generated documentation is isolated under `docs/` + +## Development Notes + +- This structure assumes IIS deployment with `public/` as the site root. +- Future application growth is expected to add controllers, views, models, repositories, and migrations without changing the overall directory shape. diff --git a/docs/state-management-patterns-mvc-starter.md b/docs/state-management-patterns-mvc-starter.md new file mode 100644 index 0000000..7f31ad4 --- /dev/null +++ b/docs/state-management-patterns-mvc-starter.md @@ -0,0 +1,26 @@ +# State Management Patterns - MVC-Starter + +**Date:** 2026-03-11T11:59:39Z + +## Overview + +This project does not implement client-side application state management in the SPA sense. State is primarily handled on the server/request side through controller properties, request data, and helper libraries. + +## Observed Patterns + +- **Request-scoped flow:** Incoming requests are routed and dispatched per request through `public/Default.asp` and `core/mvc.asp`. +- **Controller-held page state:** Controllers expose simple properties such as `useLayout` and `Title` to influence rendering behavior. +- **Flash messaging:** Shared layout calls `Flash().ShowErrorsIfPresent` and `Flash().ShowSuccessIfPresent`, indicating transient server-side UI messaging. +- **Configuration-driven behavior:** Runtime flags and UI behavior are sourced from `public/web.config` via `GetAppSetting`. +- **Form/cache helpers available:** `core/lib.FormCache.asp` and related helpers suggest server-side request/form support patterns. + +## What Is Not Present + +- No Redux, Context API, Vuex, MobX, Zustand, or similar client-state library +- No SPA store layer +- No separate API/client state synchronization layer + +## Brownfield Notes + +- Treat this app as server-rendered and request-driven. +- If richer client-side interactivity is added later, it will be a new architectural layer rather than an extension of an existing state-management system. diff --git a/docs/technology-stack.md b/docs/technology-stack.md new file mode 100644 index 0000000..cba95e2 --- /dev/null +++ b/docs/technology-stack.md @@ -0,0 +1,28 @@ +# Technology Stack + +**Date:** 2026-03-11T11:59:39Z + +## Part: MVC-Starter + +| Category | Technology | Version / Variant | Justification | +|---|---|---|---| +| Runtime | Classic ASP | legacy IIS-hosted runtime | Request entrypoint is `public/Default.asp`, with `.asp` controllers, views, and framework files across `app/` and `core/`. | +| Language | VBScript | Classic ASP server-side VBScript | Controllers, framework code, helpers, and operational scripts are written in VBScript. | +| Web Server | IIS | Windows IIS with URL Rewrite | `public/web.config` configures default document behavior and rewrite rules to route requests through `Default.asp`. | +| MVC Framework | RouteKit Classic ASP | project-local framework/starter | README identifies RouteKit Classic ASP; routing and dispatch are implemented by `router.wsc`, `core/mvc.asp`, and controller registry logic. | +| Routing | RouteKit router + IIS rewrite | custom runtime routing | `router.AddRoute` is used in `public/Default.asp`, and incoming requests are rewritten by IIS before dispatch. | +| Data Access | ADO / OLE DB | Classic ASP ADODB stack | DAL and repository generation rely on ADO connections and Classic ASP data access libraries. | +| Database | Microsoft Access | `Microsoft.ACE.OLEDB.12.0` provider | `public/web.config` configures an `.accdb` connection string; `db/webdata.accdb` is included in the repo. | +| Database Migration | Custom VBScript migration runner | project-local | `scripts/runMigrations.vbs` manages migration application, rollback, and status outside IIS. | +| UI Framework | Bootstrap | 5.3.3 | Shared header loads Bootstrap CSS and JS from CDN for layout and components. | +| Icon Library | Bootstrap Icons | 1.11.3 | Shared header loads Bootstrap Icons from CDN. | +| Frontend Rendering | Server-rendered HTML views | Classic ASP include-based rendering | Controllers include `.asp` view files, with shared header/footer layout wrapping page content. | +| Tooling | Windows Script Host VBScript scripts | project-local generators | `scripts/generateController.vbs`, `GenerateRepo.vbs`, `generateMigration.vbs`, and `runMigrations.vbs` drive scaffolding and DB workflow. | +| Configuration | IIS `web.config` + appSettings | XML-based runtime config | Connection strings, environment flags, cache settings, flash timing, and other behavior are driven from `public/web.config`. | + +## Stack Notes + +- This is a server-rendered web app, not a separate frontend/backend split stack. +- The repo has no `package.json`, `requirements.txt`, or other modern package manifest for the application itself. +- Frontend assets are primarily CDN-hosted Bootstrap resources plus local static assets served from `public/`. +- Development and operational tooling assume a Windows environment with IIS and Windows Script Host. diff --git a/docs/ui-component-inventory-mvc-starter.md b/docs/ui-component-inventory-mvc-starter.md new file mode 100644 index 0000000..15f2b15 --- /dev/null +++ b/docs/ui-component-inventory-mvc-starter.md @@ -0,0 +1,50 @@ +# UI Component Inventory - MVC-Starter + +**Date:** 2026-03-11T11:59:39Z + +## Overview + +The current UI surface is small and server-rendered. Reuse is primarily achieved through shared layout files and Bootstrap utility/component classes rather than a formal component library. + +## Shared UI Surfaces + +### Shared Layout + +- `app/views/shared/header.asp` + - Sets page charset/codepage + - Resolves the page title from `CurrentController.Title` + - Loads Bootstrap 5.3.3 and Bootstrap Icons 1.11.3 from CDN + - Renders the top navigation shell + - Displays flash error/success messages + +- `app/views/shared/footer.asp` + - Closes the main layout wrapper + - Loads the Bootstrap JS bundle from CDN + +## Feature Views + +### Home View + +- `app/views/Home/index.asp` + - Welcome/placeholder landing page + - Uses Bootstrap cards, grid, typography, and code snippets + - Documents where new controllers, repositories, and views belong + +### Error View + +- `app/views/Error/NotFound.asp` + - 404 page with countdown redirect behavior + - Uses Bootstrap card layout and iconography + - Reads redirect timing from config via `GetAppSetting("Error404RedirectSeconds")` + +## Reusable UI Patterns + +- Shared header/footer layout wrapping +- Bootstrap-based cards and grid layout +- Config-driven flash messages in shared layout +- Simple server-rendered page title convention via controller property + +## Brownfield Notes + +- Reuse is currently layout-oriented, not component-library oriented. +- New UI work will likely create additional `.asp` view files under `app/views/` rather than reusable frontend components in a JS framework. diff --git a/docs/user-provided-context.md b/docs/user-provided-context.md new file mode 100644 index 0000000..422d801 --- /dev/null +++ b/docs/user-provided-context.md @@ -0,0 +1,13 @@ +# User-Provided Context + +**Date:** 2026-03-11T11:59:39Z + +No additional documents, paths, or focus areas were provided by the user for this documentation run. + +## Implication for This Scan + +The scan should prioritize: + +- the main application structure under `public/`, `core/`, `app/`, `db/`, and `scripts/` +- existing first-party guidance in `README.md` +- BMAD-related project instructions only where they affect project understanding or AI-assisted maintenance diff --git a/tests/PlainRunnerTheme.asp b/tests/PlainRunnerTheme.asp new file mode 100644 index 0000000..da9198a --- /dev/null +++ b/tests/PlainRunnerTheme.asp @@ -0,0 +1,190 @@ +<% +Class PlainRunnerTheme + Public Sub Render(ByRef objRunner) +%> + + + + + MVC-Starter Test Runner + + + +

MVC-Starter Test Runner

+

Dev-only aspunit runner for the separate tests/ IIS application.

+ +
Running tests...
+
+ + + + +<% + End Sub + + Private Function GetPagesAsJSArray(ByRef pages) + Dim strReturn, i + + strReturn = "" + For i = 0 To (pages.Count - 1) + strReturn = strReturn & """" & Replace(pages.Item(i), """", "\""") & """" + If i < (pages.Count - 1) Then + strReturn = strReturn & ", " + End If + Next + + GetPagesAsJSArray = strReturn + End Function +End Class +%> diff --git a/tests/aspunit/LICENSE-MIT b/tests/aspunit/LICENSE-MIT new file mode 100644 index 0000000..df2f8a6 --- /dev/null +++ b/tests/aspunit/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2013 R. Peter Clark, Inc. http://www.rpeterclark.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/tests/aspunit/Lib/ASPUnit.asp b/tests/aspunit/Lib/ASPUnit.asp new file mode 100644 index 0000000..ef96c9b --- /dev/null +++ b/tests/aspunit/Lib/ASPUnit.asp @@ -0,0 +1,12 @@ + + + + + + +<% + Dim ASPUnit + Set ASPUnit = New ASPUnitLibrary + + ASPUnit.Task = Request.QueryString("task") +%> \ No newline at end of file diff --git a/tests/aspunit/Lib/classes/ASPUnitJSONResponder.asp b/tests/aspunit/Lib/classes/ASPUnitJSONResponder.asp new file mode 100644 index 0000000..f95c685 --- /dev/null +++ b/tests/aspunit/Lib/classes/ASPUnitJSONResponder.asp @@ -0,0 +1,123 @@ +<% + Class ASPUnitJSONResponder + Private _ + m_Serializer + + Private Sub Class_Initialize() + Set m_Serializer = New ASPUnitScenarioSerializer + End Sub + + Private Sub Class_Terminate() + Set m_Serializer = Nothing + End Sub + + Public Property Set Serializer(objValue) + Set m_Serializer = objValue + End Property + + Public Sub Respond(objModules) + Response.ContentType = "application/json" + Response.Write m_Serializer.ToJSON(objModules) + End Sub + End Class + + ' Private Classes + + Class ASPUnitScenarioSerializer + Function ToJSON(objScenario) + Dim objStream, _ + objModule, _ + objTest, _ + i, j, _ + strReturn + + Set objStream = Server.CreateObject("ADODB.Stream") + + objStream.Type = 2 ' adTypeText + objStream.Mode = 3 ' adModeReadWrite + objStream.Open + + Call objStream.WriteText("{") + Call objStream.WriteText(JSONNumberPair("testCount", objScenario.TestCount) & ",") + Call objStream.WriteText(JSONNumberPair("passCount", objScenario.PassCount) & ",") + Call objStream.WriteText(JSONBooleanPair("passed", objScenario.Passed) & ",") + Call objStream.WriteText(JSONString("modules") & ":[") + For i = 0 To (objScenario.Modules.Count - 1) + Set objModule = objScenario.Modules.Item(i) + + Call objStream.WriteText("{") + Call objStream.WriteText(JSONStringPair("name", objModule.Name) & ",") + Call objStream.WriteText(JSONNumberPair("testCount", objModule.TestCount) & ",") + Call objStream.WriteText(JSONNumberPair("passCount", objModule.PassCount) & ",") + Call objStream.WriteText(JSONNumberPair("failCount", (objModule.FailCount)) & ",") + Call objStream.WriteText(JSONBooleanPair("passed", objModule.Passed) & ",") + Call objStream.WriteText(JSONNumberPair("duration", objModule.Duration) & ",") + Call objStream.WriteText(JSONString("tests") & ":[") + For j = 0 To (objModule.Tests.Count - 1) + Set objTest = objModule.Tests.Item(j) + Call objStream.WriteText("{") + Call objStream.WriteText(JSONStringPair("name", objTest.Name) & ",") + Call objStream.WriteText(JSONBooleanPair("passed", objTest.Passed) & ",") + Call objStream.WriteText(JSONStringPair("description", objTest.Description)) + Call objStream.WriteText("}") + + If j < (objModule.Tests.Count - 1) Then + Call objStream.WriteText(",") + End If + + Set objTest = Nothing + Next + Call objStream.WriteText("]") + Call objStream.WriteText("}") + + If i < (objScenario.Modules.Count - 1) Then + Call objStream.WriteText(",") + End If + + Set objModule = Nothing + Next + Call objStream.WriteText("]") + Call objStream.WriteText("}") + + objStream.Position = 0 + + strReturn = objStream.ReadText() + + objStream.Close + Set objStream = Nothing + + ToJSON = strReturn + End Function + + Private Function JSONString(strValue) + JSONString = """" & JSONStringEscape(strValue) & """" + End Function + + Private Function JSONStringPair(strName, strValue) + JSONStringPair = JSONString(strName) & ":" & JSONString(strValue) + End Function + + Private Function JSONNumberPair(strName, varValue) + JSONNumberPair = JSONString(strName) & ":" & varValue + End Function + + Private Function JSONBooleanPair(strName, blnValue) + JSONBooleanPair = JSONString(strName) & ":" & LCase(blnValue) + End Function + + Private Function JSONStringEscape(strValue) + Dim strReturn + + strReturn = strValue + + strReturn = Replace(strReturn, "\", "\\") + strReturn = Replace(strReturn, """", "\""") + strReturn = Replace(strReturn, vbLf, "\n") + strReturn = Replace(strReturn, vbCr, "\n") + strReturn = Replace(strReturn, vbCrLf, "\n") + strReturn = Replace(strReturn, vbTab, "\t") + + JSONStringEscape = strReturn + End Function + End Class +%> \ No newline at end of file diff --git a/tests/aspunit/Lib/classes/ASPUnitLibrary.asp b/tests/aspunit/Lib/classes/ASPUnitLibrary.asp new file mode 100644 index 0000000..9c89a33 --- /dev/null +++ b/tests/aspunit/Lib/classes/ASPUnitLibrary.asp @@ -0,0 +1,104 @@ +<% + Class ASPUnitLibrary + Private _ + m_Runner, _ + m_Tester, _ + m_Task + + Public _ + Version + + Private Sub Class_Initialize() + Version = "0.1.0" + + Set m_Runner = New ASPUnitRunner + Set m_Tester = New ASPUnitTester + End Sub + + Private Sub Class_Terminate() + Set m_Runner = Nothing + Set m_Tester = Nothing + End Sub + + Public Property Set Runner(ByRef objValue) + Set m_Runner = objValue + End Property + + Public Property Set Tester(ByRef objValue) + Set m_Tester = objValue + End Property + + Public Property Let Task(strTask) + m_Task = strTask + End Property + + Public Sub Run() + Select Case UCase(m_Task) + Case "TEST" + Call m_Tester.Run() + Case Else + Call m_Runner.Run() + End Select + End Sub + + ' Test Service Facade + + Public Property Set Responder(ByRef objValue) + Set m_Tester.Responder = objValue + End Property + + Public Function CreateModule(strName, arrTests, objLifecycle) + Set CreateModule = m_Tester.CreateModule(strName, arrTests, objLifecycle) + End Function + + Public Function CreateTest(strName) + Set CreateTest = m_Tester.CreateTest(strName) + End Function + + Public Function CreateLifecycle(strSetup, strTeardown) + Set CreateLifecycle = m_Tester.CreateLifecycle(strSetup, strTeardown) + End Function + + Public Sub AddModule(objModule) + Call m_Tester.AddModule(objModule) + End Sub + + Public Sub AddModules(arrModules) + Call m_Tester.AddModules(arrModules) + End Sub + + Public Function Ok(blnResult, strDescription) + Call m_Tester.Ok(blnResult, strDescription) + End Function + + Public Function Equal(varActual, varExpected, strDescription) + Call m_Tester.Equal(varActual, varExpected, strDescription) + End Function + + Public Function NotEqual(varActual, varExpected, strDescription) + Call m_Tester.NotEqual(varActual, varExpected, strDescription) + End Function + + Public Function Same(varActual, varExpected, strDescription) + Call m_Tester.Same(varActual, varExpected, strDescription) + End Function + + Public Function NotSame(varActual, varExpected, strDescription) + Call m_Tester.NotSame(varActual, varExpected, strDescription) + End Function + + ' UI Service Facade + + Public Property Set Theme(ByRef objValue) + Set m_Runner.Theme = objValue + End Property + + Public Sub AddPage(strPage) + Call m_Runner.AddPage(strPage) + End Sub + + Public Sub AddPages(arrPages) + Call m_Runner.AddPages(arrPages) + End Sub + End Class +%> \ No newline at end of file diff --git a/tests/aspunit/Lib/classes/ASPUnitRunner.asp b/tests/aspunit/Lib/classes/ASPUnitRunner.asp new file mode 100644 index 0000000..974895c --- /dev/null +++ b/tests/aspunit/Lib/classes/ASPUnitRunner.asp @@ -0,0 +1,174 @@ +<% + Class ASPUnitRunner + Private _ + m_Theme, _ + m_Pages + + Private Sub Class_Initialize() + Set m_Theme = New ASPUnitUIModern + Set m_Pages = Server.CreateObject("System.Collections.ArrayList") + End Sub + + Private Sub Class_Terminate() + Set m_Pages = Nothing + Set m_Theme = Nothing + End Sub + + Public Property Set Theme(ByRef objValue) + Set m_Theme = objValue + End Property + + Public Property Get Pages + Set Pages = m_Pages + End Property + + ' Public methods to specify test pages + + Public Sub AddPage(strPage) + Call m_Pages.Add(strPage) + End Sub + + Public Sub AddPages(arrPages) + Dim i + + For i = 0 To UBound(arrPages) + Call AddPage(arrPages(i)) + Next + End Sub + + ' Method to run UI + + Public Sub Run() + If m_Pages.Count = 0 Then + Call AddCurrentPage() + End If + + Call m_Theme.Render(Me) + End Sub + + Private Sub AddCurrentPage() + Call AddPage(Request.ServerVariables("URL")) + End Sub + + Public Sub RenderJSLib() %> + <% + End Sub + + Public Sub RenderJSInit() %> + <% + End Sub + + Private Function GetPagesAsJSString() + Dim strReturn, _ + i + + strReturn = "" + For i = 0 To (m_Pages.Count - 1) + strReturn = strReturn & "'" & m_Pages.Item(i) & "'" + If i < (m_Pages.Count - 1) Then + strReturn = strReturn & ", " + End If + Next + + GetPagesAsJSString = strReturn + End Function + End Class +%> \ No newline at end of file diff --git a/tests/aspunit/Lib/classes/ASPUnitTester.asp b/tests/aspunit/Lib/classes/ASPUnitTester.asp new file mode 100644 index 0000000..887e9f2 --- /dev/null +++ b/tests/aspunit/Lib/classes/ASPUnitTester.asp @@ -0,0 +1,258 @@ +<% + Class ASPUnitTester + Private _ + m_Responder, _ + m_Scenario + + Private _ + m_CurrentModule, _ + m_CurrentTest + + Private Sub Class_Initialize() + Set m_Responder = New ASPUnitJSONResponder + Set m_Scenario = New ASPUnitScenario + End Sub + + Private Sub Class_Terminate() + Set m_Scenario = Nothing + Set m_Responder = Nothing + End Sub + + Public Property Set Responder(ByRef objValue) + Set m_Responder = objValue + End Property + + Public Property Get Modules() + Set Modules = m_Scenario.Modules + End Property + + ' Factory methods for private classes + + Public Function CreateModule(strName, arrTests, objLifecycle) + Dim objReturn, _ + i + + Set objReturn = New ASPUnitModule + objReturn.Name = strName + For i = 0 To UBound(arrTests) + objReturn.Tests.Add(arrTests(i)) + Next + Set objReturn.Lifecycle = objLifecycle + + Set CreateModule = objReturn + End Function + + Public Function CreateTest(strName) + Dim objReturn + + Set objReturn = New ASPUnitTest + objReturn.Name = strName + + Set CreateTest = objReturn + End Function + + Public Function CreateLifecycle(strSetup, strTeardown) + Dim objReturn + + Set objReturn = New ASPUnitTestLifecycle + objReturn.Setup = strSetup + objReturn.Teardown = strTeardown + + Set CreateLifecycle = objReturn + End Function + + ' Public methods to add modules + + Public Sub AddModule(objModule) + Call m_Scenario.Modules.Add(objModule) + End Sub + + Public Sub AddModules(arrModules) + Dim i + + For i = 0 To UBound(arrModules) + Call AddModule(arrModules(i)) + Next + End Sub + + ' Assertion Methods + + Private Function Assert(blnResult, strDescription) + If IsObject(m_CurrentTest) Then + m_CurrentTest.Passed = blnResult + m_CurrentTest.Description = strDescription + End If + + Assert = blnResult + End Function + + Public Function Ok(blnResult, strDescription) + Ok = Assert(blnResult, strDescription) + End Function + + Public Function Equal(varActual, varExpected, strDescription) + Equal = Assert((varActual = varExpected), strDescription) + End Function + + Public Function NotEqual(varActual, varExpected, strDescription) + NotEqual = Assert(Not (varActual = varExpected), strDescription) + End Function + + Public Function Same(varActual, varExpected, strDescription) + Same = Assert((varActual Is varExpected), strDescription) + End Function + + Public Function NotSame(varActual, varExpected, strDescription) + NotSame = Assert(Not (varActual Is varExpected), strDescription) + End Function + + ' Methods to run module tests + + Public Sub Run() + Dim objModule, _ + i + + For i = 0 To (m_Scenario.Modules.Count - 1) + Set objModule = m_Scenario.Modules.Item(i) + Call RunModule(objModule) + + m_Scenario.TestCount = m_Scenario.TestCount + objModule.TestCount + m_Scenario.PassCount = m_Scenario.PassCount + objModule.PassCount + m_Scenario.FailCount = m_Scenario.FailCount + objModule.FailCount + + Set objModule = Nothing + Next + + m_Responder.Respond(m_Scenario) + End Sub + + Private Sub RunModule(ByRef objModule) + Dim intTimeStart, _ + intTimeEnd, _ + objTest, _ + i + + Set m_CurrentModule = objModule + + intTimeStart = Timer + For i = 0 To (objModule.Tests.Count - 1) + Set objTest = objModule.Tests.Item(i) + + Call RunTestModuleSetup(objModule) + Call RunModuleTest(objTest) + Call RunTestModuleTeardown(objModule) + + If objTest.Passed Then + objModule.PassCount = objModule.PassCount + 1 + End If + + Set objTest = Nothing + Next + intTimeEnd = Timer + + objModule.Duration = Round((intTimeEnd - intTimestart), 3) + + Set m_CurrentModule = Nothing + End Sub + + Private Sub RunModuleTest(ByRef objTest) + Set m_CurrentTest = objTest + + On Error Resume Next + Call GetRef(objTest.Name)() + + If Err.Number <> 0 Then + Call Assert(False, Err.Description) + End If + + Err.Clear() + On Error Goto 0 + + Set m_CurrentTest = Nothing + End Sub + + Private Sub RunTestModuleSetup(ByRef objModule) + If Not objModule.Lifecycle Is Nothing Then + If Not objModule.Lifecycle.Setup = Empty Then + Call GetRef(objModule.Lifecycle.Setup)() + End If + End If + End Sub + + Private Sub RunTestModuleTeardown(ByRef objModule) + If Not objModule.Lifecycle Is Nothing Then + If Not objModule.Lifecycle.Teardown = Empty Then + Call GetRef(objModule.Lifecycle.Teardown)() + End If + End If + End Sub + End Class + + ' Private Classses + + Class ASPUnitScenario + Public _ + Modules, _ + TestCount, _ + PassCount, _ + FailCount + + Private Sub Class_Initialize() + Set Modules = Server.CreateObject("System.Collections.ArrayList") + PassCount = 0 + TestCount = 0 + FailCount = 0 + End Sub + + Private Sub Class_Terminate() + Set Modules = Nothing + End Sub + + Public Property Get Passed + Passed = (PassCount = TestCount) + End Property + End Class + + Class ASPUnitModule + Public _ + Name, _ + Tests, _ + Lifecycle, _ + Duration, _ + PassCount + + Private Sub Class_Initialize() + Set Tests = Server.CreateObject("System.Collections.ArrayList") + PassCount = 0 + End Sub + + Private Sub Class_Terminate() + Set Tests = Nothing + End Sub + + Public Property Get TestCount + TestCount = Tests.Count() + End Property + + Public Property Get FailCount + FailCount = (TestCount - PassCount) + End Property + + Public Property Get Passed + Passed = (PassCount = TestCount) + End Property + End Class + + Class ASPUnitTest + Public _ + Name, _ + Passed, _ + Description + End Class + + Class ASPUnitTestLifecycle + Public _ + Setup, _ + Teardown + End Class +%> \ No newline at end of file diff --git a/tests/aspunit/Lib/classes/ASPUnitUIModern.asp b/tests/aspunit/Lib/classes/ASPUnitUIModern.asp new file mode 100644 index 0000000..d09f06a --- /dev/null +++ b/tests/aspunit/Lib/classes/ASPUnitUIModern.asp @@ -0,0 +1,395 @@ +<% + Class ASPUnitUIModern + Public Sub Render(ByRef objRunner) %> + + + ASPUnit + + + + + + + + +
+ + +
+
+
+ + +
+ + + + + + + + + + + + + <%= objRunner.RenderJSLib() %> + + + + <%= objRunner.RenderJSInit() %> + + <% + End Sub + End Class +%> \ No newline at end of file diff --git a/tests/bootstrap.asp b/tests/bootstrap.asp new file mode 100644 index 0000000..809d47f --- /dev/null +++ b/tests/bootstrap.asp @@ -0,0 +1,49 @@ + + + +<% +Dim router + +Function ResolveProjectPath(relativePath) + Dim fso, currentFolder, testsRoot, projectRoot + + Set fso = Server.CreateObject("Scripting.FileSystemObject") + currentFolder = Server.MapPath(".") + + If LCase(fso.GetFileName(currentFolder)) = "tests" Then + testsRoot = currentFolder + Else + testsRoot = fso.GetParentFolderName(currentFolder) + End If + + projectRoot = fso.GetParentFolderName(testsRoot) + ResolveProjectPath = fso.BuildPath(projectRoot, relativePath) + + Set fso = Nothing +End Function + +Sub ResetTestRuntime() + On Error Resume Next + ControllerRegistry_Class__Singleton = Empty + Set router = Nothing + On Error GoTo 0 +End Sub + +Sub EnsureTestRouter() + If (Not IsObject(router)) Then + Set router = GetObject("script:" & ResolveProjectPath("core\\router.wsc")) + ElseIf router Is Nothing Then + Set router = GetObject("script:" & ResolveProjectPath("core\\router.wsc")) + End If +End Sub + +Sub RegisterDefaultRoutes() + Call EnsureTestRouter() + Call router.AddRoute("GET", "/home", "homeController", "Index") + Call router.AddRoute("GET", "/", "homeController", "Index") + Call router.AddRoute("GET", "", "homeController", "Index") + Call router.AddRoute("GET", "/404", "ErrorController", "NotFound") +End Sub + +Call ResetTestRuntime() +%> diff --git a/tests/component/TestHomeController.asp b/tests/component/TestHomeController.asp new file mode 100644 index 0000000..bd84754 --- /dev/null +++ b/tests/component/TestHomeController.asp @@ -0,0 +1,43 @@ + + + + +<% +Call ASPUnit.AddModule( _ + ASPUnit.CreateModule( _ + "Home Controller Component Tests", _ + Array( _ + ASPUnit.CreateTest("HomeControllerDefaultsToUsingLayout"), _ + ASPUnit.CreateTest("HomeControllerDefaultTitleIsHome") _ + ), _ + ASPUnit.CreateLifeCycle("SetupHomeController", "TeardownHomeController") _ + ) _ +) + +Call ASPUnit.Run() + +Sub SetupHomeController() + Call ResetTestRuntime() + On Error Resume Next + HomeController_Class__Singleton = Empty + On Error GoTo 0 + Call ExecuteGlobal("Dim objHomeController") + Set objHomeController = HomeController() +End Sub + +Sub TeardownHomeController() + Set objHomeController = Nothing + On Error Resume Next + HomeController_Class__Singleton = Empty + On Error GoTo 0 + Call ResetTestRuntime() +End Sub + +Function HomeControllerDefaultsToUsingLayout() + Call ASPUnit.Ok(objHomeController.useLayout, "HomeController should default to layout-enabled rendering") +End Function + +Function HomeControllerDefaultTitleIsHome() + Call ASPUnit.Equal(objHomeController.Title, "Home", "HomeController should expose the expected default title") +End Function +%> diff --git a/tests/component/web.config b/tests/component/web.config new file mode 100644 index 0000000..ed300d3 --- /dev/null +++ b/tests/component/web.config @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/tests/integration/TestMvcDispatch.asp b/tests/integration/TestMvcDispatch.asp new file mode 100644 index 0000000..9a8d803 --- /dev/null +++ b/tests/integration/TestMvcDispatch.asp @@ -0,0 +1,80 @@ + + + + +<% +Class TestDispatchController_Class + Private m_useLayout + + Private Sub Class_Initialize() + m_useLayout = False + End Sub + + Public Property Get useLayout + useLayout = m_useLayout + End Property + + Public Property Let useLayout(value) + m_useLayout = value + End Property + + Public Sub Smoke() + dispatchActionRan = True + End Sub +End Class + +Dim TestDispatchController_Class__Singleton +Function TestDispatchController() + If IsEmpty(TestDispatchController_Class__Singleton) Then + Set TestDispatchController_Class__Singleton = New TestDispatchController_Class + End If + Set TestDispatchController = TestDispatchController_Class__Singleton +End Function + +Call ASPUnit.AddModule( _ + ASPUnit.CreateModule( _ + "MVC Dispatch Smoke Tests", _ + Array( _ + ASPUnit.CreateTest("RootRouteResolvesToHomeController"), _ + ASPUnit.CreateTest("KnownRouteDispatchCompletesWithoutLookupFailure") _ + ), _ + ASPUnit.CreateLifeCycle("SetupMvcDispatch", "TeardownMvcDispatch") _ + ) _ +) + +Call ASPUnit.Run() + +Sub SetupMvcDispatch() + Call ResetTestRuntime() + On Error Resume Next + MVC_Dispatcher_Class__Singleton = Empty + TestDispatchController_Class__Singleton = Empty + On Error GoTo 0 + Call ExecuteGlobal("Dim dispatchActionRan") + dispatchActionRan = False + Call RegisterDefaultRoutes() + Call ControllerRegistry().RegisterController("testdispatchcontroller") + Call router.AddRoute("GET", "/dispatch-smoke", "testdispatchcontroller", "Smoke") +End Sub + +Sub TeardownMvcDispatch() + On Error Resume Next + MVC_Dispatcher_Class__Singleton = Empty + TestDispatchController_Class__Singleton = Empty + Response.Status = "200 OK" + On Error GoTo 0 + Call ResetTestRuntime() +End Sub + +Function RootRouteResolvesToHomeController() + Dim routeArray + routeArray = router.Resolve("GET", "/") + + Call ASPUnit.Ok((LCase(routeArray(0)) = "homecontroller" And LCase(routeArray(1)) = "index"), "Root route should resolve to HomeController.Index") +End Function + +Function KnownRouteDispatchCompletesWithoutLookupFailure() + Call MVC().DispatchRequest("GET", "/dispatch-smoke") + Call ASPUnit.Ok(dispatchActionRan, "Dispatch should reach a registered controller action without whitelist or lookup failures") +End Function +%> diff --git a/tests/integration/web.config b/tests/integration/web.config new file mode 100644 index 0000000..ed300d3 --- /dev/null +++ b/tests/integration/web.config @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/tests/run-all.asp b/tests/run-all.asp new file mode 100644 index 0000000..0abc77e --- /dev/null +++ b/tests/run-all.asp @@ -0,0 +1,9 @@ + + + + +<% +Set ASPUnit.Theme = New PlainRunnerTheme +Call RegisterTestPages() +Call ASPUnit.Run() +%> diff --git a/tests/sync-webconfigs.vbs b/tests/sync-webconfigs.vbs new file mode 100644 index 0000000..a56f47f --- /dev/null +++ b/tests/sync-webconfigs.vbs @@ -0,0 +1,25 @@ +Option Explicit + +Dim fso +Set fso = CreateObject("Scripting.FileSystemObject") + +Dim testsRoot +testsRoot = fso.GetParentFolderName(WScript.ScriptFullName) + +Dim sourceFile +sourceFile = fso.BuildPath(testsRoot, "web.config") + +If Not fso.FileExists(sourceFile) Then + WScript.Echo "Source config not found: " & sourceFile + WScript.Quit 1 +End If + +Call CopyConfig(sourceFile, fso.BuildPath(fso.BuildPath(testsRoot, "unit"), "web.config")) +Call CopyConfig(sourceFile, fso.BuildPath(fso.BuildPath(testsRoot, "component"), "web.config")) +Call CopyConfig(sourceFile, fso.BuildPath(fso.BuildPath(testsRoot, "integration"), "web.config")) + +WScript.Echo "Mirrored test web.config files updated." + +Sub CopyConfig(sourcePath, targetPath) + Call fso.CopyFile(sourcePath, targetPath, True) +End Sub diff --git a/tests/test-manifest.asp b/tests/test-manifest.asp new file mode 100644 index 0000000..3e4f1f7 --- /dev/null +++ b/tests/test-manifest.asp @@ -0,0 +1,10 @@ +<% +Sub RegisterTestPages() + Call ASPUnit.AddPages(Array( _ + "unit/TestHelpers.asp", _ + "unit/TestControllerRegistry.asp", _ + "component/TestHomeController.asp", _ + "integration/TestMvcDispatch.asp" _ + )) +End Sub +%> diff --git a/tests/unit/TestControllerRegistry.asp b/tests/unit/TestControllerRegistry.asp new file mode 100644 index 0000000..2ec905f --- /dev/null +++ b/tests/unit/TestControllerRegistry.asp @@ -0,0 +1,48 @@ + + + +<% +Call ASPUnit.AddModule( _ + ASPUnit.CreateModule( _ + "Controller Registry Tests", _ + Array( _ + ASPUnit.CreateTest("RegisteredHomeControllerIsValid"), _ + ASPUnit.CreateTest("RegisteredErrorControllerIsValid"), _ + ASPUnit.CreateTest("InvalidControllerFormatIsRejected"), _ + ASPUnit.CreateTest("InvalidActionFormatIsRejected"), _ + ASPUnit.CreateTest("UnknownControllerIsRejected") _ + ), _ + ASPUnit.CreateLifeCycle("SetupControllerRegistry", "TeardownControllerRegistry") _ + ) _ +) + +Call ASPUnit.Run() + +Sub SetupControllerRegistry() + Call ResetTestRuntime() +End Sub + +Sub TeardownControllerRegistry() + Call ResetTestRuntime() +End Sub + +Function RegisteredHomeControllerIsValid() + Call ASPUnit.Ok(ControllerRegistry().IsValidController("homecontroller"), "HomeController should be present in the whitelist") +End Function + +Function RegisteredErrorControllerIsValid() + Call ASPUnit.Ok(ControllerRegistry().IsValidController("errorcontroller"), "ErrorController should be present in the whitelist") +End Function + +Function InvalidControllerFormatIsRejected() + Call ASPUnit.Ok((Not ControllerRegistry().IsValidControllerFormat("home-controller")), "Controller names with dashes should be rejected") +End Function + +Function InvalidActionFormatIsRejected() + Call ASPUnit.Ok((Not ControllerRegistry().IsValidActionFormat("show-item")), "Action names with dashes should be rejected") +End Function + +Function UnknownControllerIsRejected() + Call ASPUnit.Ok((Not ControllerRegistry().IsValidController("missingcontroller")), "Unknown controllers should not pass whitelist checks") +End Function +%> diff --git a/tests/unit/TestHelpers.asp b/tests/unit/TestHelpers.asp new file mode 100644 index 0000000..1144cdb --- /dev/null +++ b/tests/unit/TestHelpers.asp @@ -0,0 +1,65 @@ + + + +<% +Call ASPUnit.AddModule( _ + ASPUnit.CreateModule( _ + "Helper Function Tests", _ + Array( _ + ASPUnit.CreateTest("TrimQueryParamsStripsQuestionString"), _ + ASPUnit.CreateTest("TrimQueryParamsStripsAmpersandSuffix"), _ + ASPUnit.CreateTest("TrimQueryParamsLeavesPathWithoutDelimiters"), _ + ASPUnit.CreateTest("SurroundStringInArrayWrapsStringValues"), _ + ASPUnit.CreateTest("SurroundStringInArrayLeavesNumericValuesUntouched"), _ + ASPUnit.CreateTest("SurroundStringInArrayLeavesArraysWithoutStringsUntouched") _ + ), _ + ASPUnit.CreateLifeCycle("SetupHelpers", "TeardownHelpers") _ + ) _ +) + +Call ASPUnit.Run() + +Sub SetupHelpers() + Call ResetTestRuntime() +End Sub + +Sub TeardownHelpers() + Call ResetTestRuntime() +End Sub + +Function TrimQueryParamsStripsQuestionString() + Call ASPUnit.Equal(TrimQueryParams("/home?id=7"), "/home", "TrimQueryParams should remove query string values after ?") +End Function + +Function TrimQueryParamsStripsAmpersandSuffix() + Call ASPUnit.Equal(TrimQueryParams("/home&debug=true"), "/home", "TrimQueryParams should remove suffix values after &") +End Function + +Function TrimQueryParamsLeavesPathWithoutDelimiters() + Call ASPUnit.Equal(TrimQueryParams("/home"), "/home", "TrimQueryParams should leave clean paths unchanged") +End Function + +Function SurroundStringInArrayWrapsStringValues() + Dim arr + arr = Array("alpha", 2) + arr = SurroundStringInArray(arr) + + Call ASPUnit.Equal(arr(0), """alpha""", "SurroundStringInArray should wrap string items in double quotes") +End Function + +Function SurroundStringInArrayLeavesNumericValuesUntouched() + Dim arr + arr = Array("alpha", 2) + arr = SurroundStringInArray(arr) + + Call ASPUnit.Equal(arr(1), 2, "SurroundStringInArray should leave non-string items unchanged") +End Function + +Function SurroundStringInArrayLeavesArraysWithoutStringsUntouched() + Dim arr + arr = Array(1, 2) + arr = SurroundStringInArray(arr) + + Call ASPUnit.Ok((arr(0) = 1 And arr(1) = 2), "SurroundStringInArray should leave arrays without string members unchanged") +End Function +%> diff --git a/tests/unit/web.config b/tests/unit/web.config new file mode 100644 index 0000000..ed300d3 --- /dev/null +++ b/tests/unit/web.config @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/tests/web.config b/tests/web.config new file mode 100644 index 0000000..ed300d3 --- /dev/null +++ b/tests/web.config @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + +