# qa-automation-tools

QA Automation Tools — `application_family: automation-testing`

## Create Test User

Interactive CLI tool that automates the **entire** process of creating a new test user for the QA environment — from sending the invitation to opening GitHub PRs and storing credentials in AWS.

### Features

- **12 supported applications** with app-specific profile types and roles
- **Multi-vendor support** — assign the user to multiple vendors in a single run
- **Subaccount support** — with optional vendor `HAS_ACCESS_TO` alongside the subaccount relation in Neo4j
- **Conditional SQL PR** — `art_relations` PR is only created for `workstation` and `oa` apps
- **Non-interactive (CI) mode** — fully scriptable via CLI flags
- **State persistence** — progress is saved after each step; resume if anything fails
- **Auto-incrementing branch names** — `AUT-create-test-user-1`, `AUT-create-test-user-2`, etc.
- **Pre-filled PR titles and descriptions** — GitHub PRs open with a formatted body containing user details

### Steps

| # | Step | Description |
|---|------|-------------|
| 1 | Gather input | Interactive prompts or CLI flags |
| 2 | Invite user | Sends invite via GraphQL API (accountSearch → VendorApplications → createIdentityV2) |
| 3 | Complete invitation | Accepts invitation via Auth0 Universal Login HTTP flow, enrolls TOTP MFA (Gmail API + HTTP) |
| 4 | Update Auth0 email | Changes the temp email to the final QA email via Auth0 Management API |
| 5 | Query Neo4j | Retrieves identity ID, Auth0 user ID, and roles |
| 6 | Determine contact ID | Clones `python-rds-utils` and finds the next available contact/profile ID |
| 7 | Neo4j Cypher PR | Generates a Cypher script and pushes a PR on `python-neo4j-cypher-scheduler` |
| 8 | SQL PR | Generates `art_relations` SQL inserts and pushes a PR on `python-rds-utils` (workstation/oa only) |
| 9 | AWS Secrets | Stores user credentials and metadata in AWS Secrets Manager |
| 10 | Auth0 metadata | Patches the Auth0 user with profile/contact IDs |
| 11 | Immediate testing | Runs Neo4j + RDS direct queries so the user is available immediately |

### Supported Applications

| App | Profile Type | Roles |
|-----|-------------|-------|
| workstation | LabelProfile | catalog, marketing, analytics, administrator, accounting, manage rights, advertising |
| insights | InsightsProfile | analytics |
| fansifter | AudienceProfile | audience |
| content | ContentProfile | admin, review_digital_audio, manage_nr_repertoire, manage_nr_ownership, view_orchard_sound_recording |
| distribution | DistributionProfile | manage_nr_deliveries, deliver_physical_audio, manage_nr_ownership |
| moneyhub | MoneyhubProfile | accounting |
| collaborators | CollaboratorsProfile | royalties |
| abacus | AbacusProfile | _(none)_ |
| publishing | PublishingProfile | admin |
| settings | SettingsProfile | _(none)_ |
| documents | DocumentsProfile | payee_management |
| oa | LabelProfile | catalog, marketing, analytics, administrator, accounting, manage rights, advertising |

### Prerequisites

- Node.js 22+ (see `.nvmrc`)
- Valid AWS session (use AWSUME)
- Access to GitHub repos: `theorchard/python-rds-utils`, `theorchard/python-neo4j-cypher-scheduler`
- Gmail API credentials configured (for invitation email extraction)

### Usage

```bash
# Interactive mode — prompts for everything
pnpm create-test-user

# Resume from saved state
pnpm create-test-user --resume

# Start from a specific step (1-11)
pnpm create-test-user --start-from 5
```

### Non-Interactive (CI) Mode

Pass the required flags (`--app`, `--vendor-id`, `--vendor-name`) to skip interactive prompts entirely:

```bash
# Minimal non-interactive
pnpm create-test-user --app workstation --vendor-id 18805 --vendor-name Allegro

# Full non-interactive with all options
pnpm create-test-user --app workstation --vendor-id 18805 \
    --vendor-name Allegro --roles catalog,analytics --is-admin \
    --first-name Test --last-name Tester --secret-key label_18805

# Multi-vendor (additional vendors as id:name pairs)
pnpm create-test-user --app workstation --vendor-id 18805 \
    --vendor-name Allegro --additional-vendors "123:Acme,456:Beta"

# Subaccount user with vendor Neo4j relation
pnpm create-test-user --app workstation --vendor-id 18805 \
    --vendor-name Allegro --is-subaccount --subaccount-id 999 \
    --subaccount-neo4j-relation
```

### CLI Flags

| Flag | Description | Default |
|------|-------------|---------|
| `-r, --resume` | Resume from previously saved state | — |
| `-s, --start-from <step>` | Start from a specific step number (1-11) | — |
| `-y, --yes` | Non-interactive mode (auto-confirm all prompts) | `false` |
| `--bundle` | Append to the most recent `AUT-create-test-user-<n>` branch instead of creating a new one (see [Bundling Multiple Users](#bundling-multiple-users-into-one-pr)) | `false` |
| `--app <app>` | Application under test | _(prompted)_ |
| `--vendor-id <id>` | Vendor ID | _(prompted)_ |
| `--vendor-name <name>` | Vendor name — used as the primary-vendor picker's initial search term | _(prompted)_ |
| `--additional-detail <detail>` | Additional detail for email prefix | _(prompted)_ |
| `--first-name <name>` | First name | `Test` |
| `--last-name <name>` | Last name | `Tester` |
| `--is-subaccount` | Flag as a subaccount user | `false` |
| `--subaccount-id <id>` | Subaccount ID (subaccount users only) | — |
| `--subaccount-neo4j-relation` | Include vendor `HAS_ACCESS_TO` alongside subaccount in Neo4j | `false` |
| `--is-admin` | Grant admin access | `false` |
| `--roles <roles>` | Comma-separated roles | _(prompted)_ |
| `--secret-key <key>` | AWS Secret key for storing user data | _(prompted)_ |
| `--additional-vendors <vendors>` | Additional vendors as `id:name` pairs (e.g. `123:Acme,456:Beta`) | — |

### Primary-vendor picker

Step 2 now opens an interactive picker (the same one the artist tool uses): it searches `accountSearch` with `--vendor-name` as the term and lists every matching vendor/subaccount. Items whose ID matches `--vendor-id` are flagged with `← matches your --vendor-id`. Picker controls: `m` (more), `r` (refine search term). In `--yes` mode the picker doesn't run — instead the script auto-picks by ID, falling back to a single name match if no ID match is found. If the picked vendor differs from `--vendor-id`, the script warns about the email-prefix mismatch (since the prefix was computed from the originally-supplied ID in step 1).

### State Persistence

State is saved after every step to `tmp/create-test-user/state.json`. If any step fails, you can resume exactly where you left off with `--resume` or jump to a specific step with `--start-from`.

### Branch & PR Conventions

- Branches are named `AUT-create-test-user-<idx>` with auto-incrementing index (scans both local and remote branches)
- PR titles follow the format: `[AUT] Add test user <email> (<app>, vendor <id>)`
- PR descriptions include a formatted table with all user details (email, app, vendor, roles, profile type, etc.)

## Create Test Artist User

Sibling CLI tool that creates a test user attached to an **Artist** (LabelParticipant) rather than a vendor/subaccount. Targets the `insights` app and attaches `INSIGHTS_BASE_ROLE` (fetched dynamically from `ArtistApplications`).

### What's different from `create-test-user`

| Aspect | `create-test-user` | `create-test-artist-user` |
|---|---|---|
| Tenant type | `ACCOUNT` / `SUBACCOUNT` | `LABEL_PARTICIPANT` |
| Selection | Vendor search + picker | Artist search by name + picker (paginated, with vendor filter + UUID paste) |
| App | Any of 12 supported apps | `insights` only |
| Role attached at invite | App-specific, fetched per vendor | Fetched via `ArtistApplications` (typically `INSIGHTS_BASE_ROLE`) |
| Neo4j `Profile.roles` | App-specific friendly names | `analytics` (from `APP_ROLES_MAP["insights"]`) |
| Steps run | 1–11 (SQL PR conditional) | 1–5, 7, 9–11 (no contact ID, no SQL PR) |
| Neo4j relation | `(Profile)-[:HAS_ACCESS_TO]->(Vendor)` | `(Profile)-[:HAS_ACCESS_TO]->(LabelParticipant)` |

### Usage

```bash
# Interactive mode — search by name, paginate / refine / filter by vendor / paste UUID
pnpm create-test-artist-user

# Interactive with preset search term
pnpm create-test-artist-user --artist-name "Bad Bunny"

# Non-interactive — UUID required (picker can't run)
pnpm create-test-artist-user \
    --artist-uuid 7523b1f4-f3b9-4eea-96ba-0a80c789d078 \
    --artist-name "Bad Bunny" \
    --yes

# Resume
pnpm create-test-artist-user --resume
```

### Interactive picker controls

When the script lists search results, the prompt accepts:

| Input | Action |
|---|---|
| `<number>` | Pick that result |
| `m` / `more` | Load the next page of results (appends; index numbers grow) |
| `r` / `refine` | Enter a new search term — replaces the current list |
| `v` / `vendor` | Filter the loaded results to artists under a specific vendor (ID or name substring) |
| `u` / `uuid` | Paste a LabelParticipant UUID directly — validated via `labelParticipantByUuid` before accepting |

### CLI Flags

| Flag | Description | Default |
|------|-------------|---------|
| `-r, --resume` | Resume from previously saved state | — |
| `-s, --start-from <step>` | Start from a specific step number | — |
| `-y, --yes` | Non-interactive mode (requires `--artist-uuid`) | `false` |
| `--bundle` | Append to the most recent `AUT-create-test-user-<n>` branch instead of creating a new one (see [Bundling Multiple Users](#bundling-multiple-users-into-one-pr)) | `false` |
| `--artist-uuid <uuid>` | Artist (LabelParticipant) UUID — skips the picker | _(picker)_ |
| `--artist-name <name>` | Artist name; used as the search term when `--artist-uuid` is absent | _(prompted)_ |
| `--additional-detail <detail>` | Detail used in email prefix | `artist_<name-slug>` |
| `--first-name <name>` | First name | `Test` |
| `--last-name <name>` | Last name | `Artist` |
| `--secret-key <key>` | AWS Secret key for storing user data | _(equals `additional-detail`)_ |

State is persisted to `tmp/create-test-artist-user/state.json`. Cypher PR branches use the shared prefix `AUT-create-test-user-<idx>` (same as the vendor tool, so both can contribute to one PR — see below).

## Bundling Multiple Users Into One PR

Both `create-test-user` and `create-test-artist-user` accept a `--bundle` flag that appends the new user to the *previous* run's PR branch instead of cutting a new branch. Profile IDs auto-increment off the file you just modified, so back-to-back users no longer collide on the same ID.

The two tools share a single branch prefix (`AUT-create-test-user-<n>`), so you can mix vendor and artist users freely in the same PR.

```bash
# Run 1 — fresh PR
pnpm create-test-user --app workstation --vendor-name "Allegro" --yes

# Run 2 — add an artist user to the SAME PR
pnpm create-test-artist-user --artist-uuid <uuid> --artist-name "Bad Bunny" --bundle --yes

# Run 3 — add another vendor user to the same PR
pnpm create-test-user --app insights --vendor-name "Some Label" --bundle --yes
```

If you pass `--bundle` but the local repo isn't on an `AUT-create-test-user-<n>` branch (e.g., still on `master` because no previous run has touched it), the script errors with a clear message — bundling needs a starting branch.

When you want a brand-new PR afterwards, run without `--bundle`: the script checks out `master`, pulls latest, and creates a new auto-incremented branch.

## Grant Label to User

Grants an existing user access to an additional vendor label (no invite, no new credentials — just the label access, surviving the QA refresh via the same two-PR model). The day-to-day path for devs who repeatedly work on the same labels. Full docs: [`src/grant-label-to-user/README.md`](src/grant-label-to-user/README.md).

## Reset OA Users

CLI tool that resets passwords for OA Cypress test users in Auth0 and updates the credentials in AWS Secrets Manager.

### Features

- **Resets all users by default** — reads all entries from the `oa` section of the secret automatically
- **Selective reset** — target specific users with `--users`
- **GSuite-aware** — users with `type: "gsuite"` are automatically skipped (SSO accounts have no Auth0 password)
- **Dry-run mode** — preview exactly what would happen without touching Auth0 or AWS
- **Non-interactive (CI) mode** — skip confirmation prompts with `--yes`
- **Password suppression** — new passwords are not printed in non-interactive mode; retrieve them from AWS Secrets Manager

### Prerequisites

- Node.js 22+ (see `.nvmrc`)
- Valid AWS session (use AWSUME)

### Usage

```bash
# Reset all OA users found in the secret
pnpm reset-oa-users

# Reset specific users only
pnpm reset-oa-users --users cypress_manager,cypress_manager_1

# Preview without making any changes
pnpm reset-oa-users --dry-run

# Skip confirmation prompt (CI / Jenkins)
pnpm reset-oa-users --yes
```

### CLI Flags

| Flag | Description | Default |
|------|-------------|---------|
| `--users <list>` | Comma-separated user keys to reset | All users in the `oa` secret section |
| `--dry-run` | Preview changes without writing to Auth0 or AWS | `false` |
| `--yes` | Skip confirmation prompts | `false` |

### Jenkins

Trigger via the `RUN_RESET_OA_USERS` parameter in Jenkins. Optionally set `RESET_USERS` to a comma-separated list of keys to limit which users are reset; leave blank to reset all.

## Docker

Both tools can be run via Docker, which is the recommended approach for CI and optional for local use.

### Prerequisites

- Docker
- Valid AWS session (`awsume <profile>`) — credentials are passed through from your shell environment
- `GITHUB_TOKEN` — GitHub personal access token with `repo` scope (required by `create-test-user` for cloning private repos and pushing branches; not needed for `reset-oa-users`)

### Build

```bash
docker build -t qa-automation-tools .
```

### Local usage

```bash
# Reset OA users (dry run — no changes made)
docker compose run --rm reset-oa-users pnpm reset-oa-users --dry-run --yes

# Reset specific users
docker compose run --rm reset-oa-users pnpm reset-oa-users --users cypress_manager,cypress_manager_1 --yes

# Create test user (non-interactive)
docker compose run --rm create-test-user pnpm create-test-user --app workstation --vendor-id 18805 --vendor-name Allegro --yes

# Resume a failed create-test-user run (state is persisted in ./tmp via volume mount)
docker compose run --rm create-test-user pnpm create-test-user --resume
```

Set `GITHUB_TOKEN`, and optionally `GIT_USER_NAME` / `GIT_USER_EMAIL`, in your shell or in a `.env` file (see `.env.example`).

### Environment variables

| Variable | Required | Description |
|----------|----------|-------------|
| `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` / `AWS_SESSION_TOKEN` | Yes | Set by `awsume` in your shell; passed through automatically |
| `GITHUB_TOKEN` | `create-test-user` only | PAT with `repo` scope |
| `GIT_USER_NAME` / `GIT_USER_EMAIL` | No | Git commit identity (defaults to `QA Automation Bot`) |
| `CONFIG_ENV` | No | Defaults to `qa` |

## Development

```bash
# Install dependencies
pnpm install

# Type check
pnpm typecheck

# Lint
pnpm lint

# Format
pnpm format

# Run tests
pnpm test
```

### CI / Jenkins

The project includes a `Jenkinsfile` with three modes:

1. **Quality checks** (default) — runs lint, format check, and tests in parallel
2. **RUN_CREATE_USER** — executes the create-test-user flow via Jenkins parameters
3. **RUN_RESET_OA_USERS** — executes the reset-oa-users flow via Jenkins parameters
