add claude.md to git
This commit is contained in:
parent
f9b575816a
commit
daa8aa3966
1 changed files with 240 additions and 0 deletions
240
CLAUDE.md
Normal file
240
CLAUDE.md
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## CRITICAL: Read AGENTS.md First
|
||||
|
||||
**Before starting any work in this repository**, you MUST read `AGENTS.md` in the project root.
|
||||
|
||||
- `AGENTS.md` contains comprehensive Phoenix/LiveView/Elixir guidelines that are mandatory for this project
|
||||
- These guidelines take precedence over general development practices when there's a conflict
|
||||
- Always read `AGENTS.md` at the start of a new conversation or when resuming work
|
||||
- Follow the project-specific patterns, conventions, and constraints documented there
|
||||
|
||||
## Project Overview
|
||||
|
||||
Towerops is a Phoenix 1.8 web application built with Elixir, using Ecto for database operations (PostgreSQL), LiveView for real-time interactions, and Tailwind CSS v4 for styling.
|
||||
|
||||
### Data Model Relationships
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ User │
|
||||
│ (Accounts) │
|
||||
└──────┬──────┘
|
||||
│ owns/member of (many-to-many)
|
||||
│
|
||||
↓
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ Organization │
|
||||
│ (Organizations) │
|
||||
└────┬────────────┬─────────────┬──────────────────────┘
|
||||
│ │ │
|
||||
│ has many │ has many │ has many
|
||||
│ │ │
|
||||
↓ ↓ ↓
|
||||
┌─────────┐ ┌──────────┐ ┌────────────────┐
|
||||
│ Site │ │Equipment │ │ AgentToken │
|
||||
│ (Sites) │ │(Equipment│ │ (Agents) │
|
||||
└────┬────┘ └────┬─────┘ └────────┬───────┘
|
||||
│ │ │
|
||||
│ has many │ has one │ assigned via
|
||||
│ │ │
|
||||
↓ ↓ ↓
|
||||
┌──────────┐ ┌──────────────┐ ┌──────────────────┐
|
||||
│Equipment │ │ SNMPDevice │ │ AgentAssignment │◀──┐
|
||||
│(Equipment│ │(Snmp.Devices)│ │ (Agents) │ │
|
||||
└────┬─────┘ └──────────────┘ └──────────────────┘ │
|
||||
│ │ │
|
||||
│ has many └───────────────┘
|
||||
├────────────────┬─────────────────┘
|
||||
│ │ assigns equipment
|
||||
↓ ↓ to agent
|
||||
┌──────────────┐ ┌─────────┐
|
||||
│MonitoringCheck│ │ Alert │
|
||||
│ (Monitoring) │ │ (Alerts)│
|
||||
└──────────────┘ └─────────┘
|
||||
|
||||
Key Relationships:
|
||||
- User can own/belong to multiple Organizations
|
||||
- Organization has default_agent_token_id (optional)
|
||||
- Equipment belongs to both Site and Organization (denormalized)
|
||||
- Equipment can be assigned to one AgentToken via AgentAssignment
|
||||
- Equipment has one SNMPDevice with Sensors and Interfaces
|
||||
- Equipment has many MonitoringChecks (polling results)
|
||||
- Equipment has many Alerts (equipment_down, equipment_up)
|
||||
- AgentToken authenticates remote agents for local SNMP polling
|
||||
```
|
||||
|
||||
**Note to AI assistants**: When you make changes to the data model (migrations, schema additions, new contexts), update this diagram to reflect the current state.
|
||||
|
||||
## Essential Commands
|
||||
|
||||
### Setup and Development
|
||||
- `mix setup` - Install dependencies, create/migrate database, setup and build assets
|
||||
- `mix phx.server` - Start the Phoenix server (accessible at http://localhost:4000)
|
||||
- `iex -S mix phx.server` - Start server with interactive Elixir shell
|
||||
|
||||
### Testing and Quality
|
||||
- `mix test` - Run all tests (automatically creates and migrates test database)
|
||||
- `mix test test/path/to/specific_test.exs` - Run a specific test file
|
||||
- `mix test --failed` - Re-run only previously failed tests
|
||||
- `mix test --cover` - Run tests with coverage report (generates HTML report in `cover/` directory)
|
||||
- `mix precommit` - **Run before committing**: compiles with warnings as errors, unlocks unused deps, formats code, and runs tests
|
||||
|
||||
**Coverage Target**: 90% minimum (currently configured threshold)
|
||||
- Coverage reports show line-by-line coverage in `cover/` directory
|
||||
- View detailed HTML report: `open cover/modules_*.html`
|
||||
- Current status: 60.40% overall (as of Jan 12, 2026, 6:35 PM)
|
||||
- Recent improvements (Session 1):
|
||||
- Towerops.Snmp: 10.87% → 97.83% (+87%)
|
||||
- Towerops.Snmp.Profiles.NetSnmp: 56.79% → 92.59% (+35.8%)
|
||||
- Towerops.Monitoring.Supervisor: 21.21% → 63.64% (+42%)
|
||||
- Towerops.Snmp.Profiles.Cisco: 20.22% → 57.30% (+37%)
|
||||
- ToweropsWeb.EquipmentLive.Show: 35.51% → 46.38% (+10.87%)
|
||||
|
||||
**Coverage Notes**:
|
||||
- Protobuf-generated modules (Towerops.Agent.*) show 0% but don't need tests
|
||||
- Focus areas for improvement (in priority order):
|
||||
- Towerops.Monitoring context (33.33%)
|
||||
- SNMP profiles: NetSnmp (56.79%), Base (69.52%), Mikrotik (77.97%)
|
||||
- LiveView modules: EquipmentLive.Show (35.51%), EquipmentLive.Form (46.73%)
|
||||
- Accounts modules: UserCredentialController (33.93%), Accounts context (61.82%)
|
||||
- 0% coverage modules (Admin, WebAuthn, etc.) - may be intentionally untested
|
||||
|
||||
### Database
|
||||
- `mix ecto.create` - Create the database
|
||||
- `mix ecto.migrate` - Run pending migrations
|
||||
- `mix ecto.reset` - Drop, create, and migrate database
|
||||
- `mix ecto.gen.migration migration_name_using_underscores` - Generate a new migration file
|
||||
|
||||
### Assets
|
||||
- `mix assets.build` - Build CSS and JS assets (Tailwind + esbuild)
|
||||
- `mix assets.deploy` - Build minified assets for production
|
||||
|
||||
## Architecture
|
||||
|
||||
### Application Structure
|
||||
|
||||
The application follows standard Phoenix conventions with clear separation between business logic (`lib/towerops/`) and web interface (`lib/towerops_web/`):
|
||||
|
||||
- **Towerops.Application** - OTP application that supervises:
|
||||
- Telemetry for metrics
|
||||
- Repo (Ecto) for database
|
||||
- DNSCluster for service discovery
|
||||
- PubSub for pub/sub messaging
|
||||
- Endpoint (web server)
|
||||
|
||||
- **ToweropsWeb** - Main web module that provides `use` macros for:
|
||||
- `:router` - Route definitions
|
||||
- `:controller` - Traditional request/response controllers
|
||||
- `:live_view` - LiveView modules
|
||||
- `:live_component` - LiveView component modules
|
||||
- `:html` - Phoenix.Component modules
|
||||
|
||||
### Key Configuration Details
|
||||
|
||||
- **Binary IDs**: Generators use binary (UUID) primary keys by default (`binary_id: true`)
|
||||
- **Timestamps**: Use `:utc_datetime` for all timestamps
|
||||
- **Web server**: Uses Bandit adapter (not Cowboy)
|
||||
- **HTTP client**: Uses `:req` library (Req module) - this is the only approved HTTP client
|
||||
- **Ecto repos**: `[Towerops.Repo]`
|
||||
|
||||
### Web Layer Patterns
|
||||
|
||||
All LiveViews, LiveComponents, and HTML modules automatically get these imports/aliases via `html_helpers/0`:
|
||||
- `ToweropsWeb.CoreComponents` - Core UI components (`<.button>`, `<.input>`, `<.form>`, etc.)
|
||||
- `ToweropsWeb.Layouts` - Layout components (aliased, no need to re-alias)
|
||||
- `Phoenix.LiveView.JS` - Client-side JS commands
|
||||
- Gettext for translations
|
||||
- Verified routes with `~p` sigil
|
||||
|
||||
### Asset Pipeline
|
||||
|
||||
- **Tailwind CSS v4**: Uses new `@import "tailwindcss"` syntax in `assets/css/app.css`, no `tailwind.config.js` needed
|
||||
- **esbuild**: Bundles JavaScript from `assets/js/app.js`
|
||||
- All vendor scripts/styles must be imported into app.js/app.css - cannot reference external src/href in layouts
|
||||
- No inline `<script>` tags in templates - use colocated LiveView hooks instead
|
||||
|
||||
### Development Environment
|
||||
|
||||
- Dev routes enabled for:
|
||||
- LiveDashboard at `/dev/dashboard`
|
||||
- Swoosh mailbox preview at `/dev/mailbox`
|
||||
- Phoenix LiveReload watches for file changes
|
||||
- Code reloader enabled via `listeners: [Phoenix.CodeReloader]`
|
||||
|
||||
## Project-Specific Constraints
|
||||
|
||||
Key constraints from AGENTS.md (see that file for complete details):
|
||||
|
||||
- Use `mix precommit` before committing changes
|
||||
- Use `:req` (Req) for all HTTP requests - never use `:httpoison`, `:tesla`, or `:httpc`
|
||||
- Never use `daisyUI` - write custom Tailwind components for world-class design
|
||||
- LiveView templates must start with `<Layouts.app flash={@flash}>`
|
||||
- Use `<.icon name="hero-x-mark">` component for icons, never Heroicons modules
|
||||
- Use LiveView streams for collections to avoid memory issues
|
||||
- Forms must use `to_form/2` in LiveView and `<.form for={@form}>` in templates
|
||||
- Never access changesets directly in templates - always use the form assign
|
||||
- Tailwind v4 uses new import syntax - never use `@apply` in CSS
|
||||
- when adding/updating hex modules, only use https://hex.pm
|
||||
- assets are rebuilt on save and don't need to be built with mix assets.build
|
||||
- be sure to always run mix format after you modify an elixir file
|
||||
- when you run mix format, don't specify a file, let it format everything
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
### SNMP Mocking with Mox
|
||||
|
||||
The application uses Mox for SNMP client mocking in tests. Key patterns:
|
||||
|
||||
**SNMP Adapter Mock Format**:
|
||||
- `snmp_adapter().get/3` returns `{:ok, value}` or `{:error, reason}`
|
||||
- `snmp_adapter().walk/3` returns `{:ok, [%{oid: "...", value: ...}]}` (list of maps, NOT a map)
|
||||
- Values from `get/3` are already extracted (no type wrapper needed in mocks)
|
||||
|
||||
**Mock Expectations**:
|
||||
```elixir
|
||||
# Mock get_multiple - it calls get/3 once per OID
|
||||
expect(SnmpMock, :get, 6, fn _target, oid, _opts ->
|
||||
case oid do
|
||||
"1.3.6.1.2.1.1.1.0" -> {:ok, "Cisco IOS Software"} # sysDescr
|
||||
"1.3.6.1.2.1.1.2.0" -> {:ok, [1, 3, 6, 1, 4, 1, 9]} # sysObjectID
|
||||
"1.3.6.1.2.1.1.3.0" -> {:ok, 12_345} # sysUpTime (integer)
|
||||
"1.3.6.1.2.1.1.4.0" -> {:ok, "admin@example.com"} # sysContact
|
||||
"1.3.6.1.2.1.1.5.0" -> {:ok, "test-device"} # sysName
|
||||
"1.3.6.1.2.1.1.6.0" -> {:ok, "Test Location"} # sysLocation
|
||||
end
|
||||
end)
|
||||
|
||||
# Mock walk - returns list of OID/value maps
|
||||
expect(SnmpMock, :walk, fn _target, _oid, _opts ->
|
||||
{:ok, [
|
||||
%{oid: "1.3.6.1.4.1.9.9.91.1.1.1.1.1.1000", value: 8}
|
||||
]}
|
||||
end)
|
||||
|
||||
# Empty walk result (no data found)
|
||||
expect(SnmpMock, :walk, fn _, _, _ ->
|
||||
{:ok, []} # NOT {:ok, %{}}
|
||||
end)
|
||||
```
|
||||
|
||||
**Common Pitfalls**:
|
||||
- ❌ Returning `{:ok, %{}}` from walk - should be `{:ok, []}`
|
||||
- ❌ Returning type wrappers like `{:integer, 123}` - values are already extracted
|
||||
- ❌ Not matching OIDs in get expectations - use pattern matching on OID string
|
||||
- ❌ Expecting wrong number of calls - `get_multiple/2` calls `get/3` once per OID
|
||||
|
||||
**TimescaleDB Tests**:
|
||||
- Tests that query continuous aggregates should be tagged with `@tag :skip`
|
||||
- Reason: Test database doesn't have TimescaleDB continuous aggregates configured
|
||||
- Example: `get_hourly_stats/3`, `get_daily_stats/3`, `get_uptime_percentage/1`
|
||||
|
||||
### Test Organization
|
||||
|
||||
- Use `DataCase` for tests that need database access
|
||||
- Use `ConnCase` for controller/LiveView tests
|
||||
- Use `async: true` for tests that can run in parallel (most unit tests)
|
||||
- Use `async: false` for tests with shared state (supervisor tests, integration tests)
|
||||
- never open the test coverage html files
|
||||
Loading…
Add table
Reference in a new issue