|
| 1 | +--- |
| 2 | +name: MCO Automate Test Workflow |
| 3 | +description: Conventions and workflow for generating new MCO e2e tests from test specifications |
| 4 | +--- |
| 5 | + |
| 6 | +# MCO Test Automation Rules |
| 7 | + |
| 8 | +Generate new e2e test code in `test/extended-priv/` from test specifications. |
| 9 | + |
| 10 | +## Input Detection |
| 11 | + |
| 12 | +- **Text file**: path exists on disk -> read and parse it for test case ID, title, preconditions, steps, expected results, tags |
| 13 | +- **Polarion ID**: matches `OCP-\d+` or purely numeric -> future (MCO-2220) |
| 14 | +- **Jira ID**: matches `MCO-\d+` -> future (MCO-2221) |
| 15 | + |
| 16 | +Only text file input is supported now. |
| 17 | + |
| 18 | +## Target File Resolution |
| 19 | + |
| 20 | +Derive from `<suite-name>`: `"mco security"` -> `mco_security.go` |
| 21 | +- If `test/extended-priv/<filename>` exists: add new `g.It()` block to it |
| 22 | +- If it doesn't exist: create new file with full test structure |
| 23 | + |
| 24 | +## Code Review Learning |
| 25 | + |
| 26 | +Learn from previous code reviews to produce review-ready code. This is cumulative — patterns are stored in memory (`review_patterns_mco.md`) and grow with every run. |
| 27 | + |
| 28 | +- Load existing patterns from memory, note the `Last Analyzed PR` as high-water mark |
| 29 | +- Fetch merged PRs that modified `test/extended-priv/` via `gh`, only newer than last analyzed |
| 30 | +- For each PR, fetch inline review comments and PR-level reviews via `gh api` |
| 31 | +- Extract actionable coding standards (naming, resource handling, error handling, code structure, common mistakes) |
| 32 | +- Deduplicate against existing patterns. Newer patterns win on contradiction |
| 33 | +- Save updated patterns to `review_patterns_mco.md` memory |
| 34 | +- Fall back to built-in conventions below if `gh` is unavailable |
| 35 | + |
| 36 | +## Codebase Context |
| 37 | + |
| 38 | +Before generating, read existing code to understand available utilities and patterns: |
| 39 | + |
| 40 | +- The target file (if existing) — its Describe block, JustBeforeEach, variables, existing tests |
| 41 | +- 2-3 similar test files based on topic |
| 42 | +- Resource wrappers: `machineconfig.go`, `machineconfigpool.go`, `node.go`, etc. |
| 43 | +- Helpers: `util.go`, `resource.go`, `remotefile.go`, `gomega_matchers.go` |
| 44 | +- Testdata templates in `test/extended-priv/testdata/files/` |
| 45 | + |
| 46 | +## Conventions |
| 47 | + |
| 48 | +1. **Use GetCompactCompatiblePool** for MCP selection, unless the test requires the master pool |
| 49 | +2. **Call GetCurrentTestPolarionIDNumber()** once per test |
| 50 | +3. **Use Generic MC template** when possible |
| 51 | +4. **Base64 encode** file configuration in MachineConfigs |
| 52 | +5. **Use test number** in file paths, content, and resource names for uniqueness |
| 53 | +6. **Node selection**: `GetSortedNodesOrFail()[0]` |
| 54 | +7. **Step logging**: `exutil.By("<step>")` for each step, `logger.Infof("OK!\n")` after success |
| 55 | +8. **Declare variables** in the `var` block when possible |
| 56 | +9. **Error messages**: log the full resource with `%s`, not just `.GetName()` |
| 57 | +10. **State recovery**: always recover initial state via defer (e.g., `defer resource.DeleteWithWait()`) |
| 58 | +11. **Prefer Resource struct methods** over `oc.AsAdmin().Run` |
| 59 | +12. **RemoteFile** with gomega checkers for verifying files inside nodes |
| 60 | +13. **Concise comments** — one or two lines maximum |
| 61 | +14. **Skip on SNO/Compact** when the test requires multiple nodes or a dedicated worker pool: `if IsCompactOrSNOCluster(oc.AsAdmin()) { g.Skip("...") }` or `exutil.SkipOnSingleNodeTopology(oc.AsAdmin())` |
| 62 | + |
| 63 | +## New File Structure |
| 64 | + |
| 65 | +```go |
| 66 | +package extended |
| 67 | + |
| 68 | +import ( |
| 69 | + g "github.com/onsi/ginkgo/v2" |
| 70 | + o "github.com/onsi/gomega" |
| 71 | + exutil "github.com/openshift/machine-config-operator/test/extended-priv/util" |
| 72 | + logger "github.com/openshift/machine-config-operator/test/extended-priv/util/logext" |
| 73 | +) |
| 74 | + |
| 75 | +var _ = g.Describe("[sig-mco][Suite:openshift/machine-config-operator/<suite-type>][Serial][Disruptive] MCO <suite-name>", func() { |
| 76 | + defer g.GinkgoRecover() |
| 77 | + |
| 78 | + var oc = exutil.NewCLI("mco-<cli-name>", exutil.KubeConfigPath()) |
| 79 | + |
| 80 | + g.JustBeforeEach(func() { |
| 81 | + PreChecks(oc) |
| 82 | + }) |
| 83 | + |
| 84 | + g.It("[PolarionID:<id>][OTP] <description> [Disruptive]", func() { |
| 85 | + // test body |
| 86 | + }) |
| 87 | +}) |
| 88 | +``` |
| 89 | + |
| 90 | +- `[Serial][Disruptive]` is **always required** on Describe blocks |
| 91 | +- `<cli-name>`: suite name hyphenated (e.g., `mco-security`) |
| 92 | + |
| 93 | +## Existing File Insertion |
| 94 | + |
| 95 | +Insert new `g.It` block inside the existing `g.Describe`, after the last test. Add new imports as needed. Do not modify existing code. |
| 96 | + |
| 97 | +If the test requires new helper functions, append them **after all existing functions** at the end of the file — never insert between existing functions. Helper functions belong outside the `g.Describe` block, after its closing `})`. |
| 98 | + |
| 99 | +## Test Body Pattern |
| 100 | + |
| 101 | +```go |
| 102 | +g.It("[PolarionID:<id>][OTP] <description> [Disruptive]", g.Label("Platform:aws"), func() { |
| 103 | + testID := GetCurrentTestPolarionIDNumber() |
| 104 | + mcp := GetCompactCompatiblePool(oc.AsAdmin()) |
| 105 | + node := mcp.GetSortedNodesOrFail()[0] |
| 106 | + |
| 107 | + exutil.By("<step 1 from spec>") |
| 108 | + // implementation |
| 109 | + logger.Infof("OK!\n") |
| 110 | + |
| 111 | + exutil.By("<step 2 from spec>") |
| 112 | + // implementation |
| 113 | + logger.Infof("OK!\n") |
| 114 | +}) |
| 115 | +``` |
| 116 | + |
| 117 | +## Generation Rules |
| 118 | + |
| 119 | +- Each spec step becomes an `exutil.By()` block |
| 120 | +- Use existing helpers — prefer utility functions over raw oc commands |
| 121 | +- Defer cleanup immediately after resource creation |
| 122 | +- Resource names include test ID: `fmt.Sprintf("test-%s-mc", testID)` |
| 123 | +- Node verification: `NewRemoteFile(node, path)` with gomega matchers |
| 124 | +- Pool waiting after changes: `mcp.waitForComplete()` |
| 125 | +- Platform labels: `g.Label("Platform:aws", "Platform:gce")` based on spec tags |
| 126 | +- Feature gate labels: `g.Label("OCPFeatureGate:XXX")` if the spec requires it |
| 127 | +- Skip functions for platform/architecture: `skipTestIfSupportedPlatformNotMatched(oc, AWSPlatform, GCPPlatform)`, `architecture.SkipNonAmd64SingleArch(oc)` |
| 128 | +- Skip on SNO/Compact: if the test needs more than one node or a dedicated worker pool, add `if IsCompactOrSNOCluster(oc.AsAdmin()) { g.Skip("This test requires multiple nodes and cannot run on SNO/Compact clusters") }` at the start of the test body |
| 129 | +- If the test requires new YAML templates, create them in `test/extended-priv/testdata/files/` and reference with `SetMCOTemplate("<name>.yaml")` |
| 130 | + |
| 131 | +## Post-Generation Verification |
| 132 | + |
| 133 | +After generating the test code, verify completeness: |
| 134 | + |
| 135 | +1. Extract every numbered step, precondition check, and expected result from the specification |
| 136 | +2. Map each to an `exutil.By()` block in the generated code |
| 137 | +3. Confirm 1:1 coverage — every spec step must have a corresponding `exutil.By()` block |
| 138 | +4. Report any missing steps and add the missing `exutil.By()` blocks before finalizing |
| 139 | + |
| 140 | +## Platform Constants |
| 141 | + |
| 142 | +| Platform | Constant | g.Label Value | |
| 143 | +|---|---|---| |
| 144 | +| AWS | `AWSPlatform` | `"Platform:aws"` | |
| 145 | +| GCP | `GCPPlatform` | `"Platform:gce"` | |
| 146 | +| Azure | `AzurePlatform` | `"Platform:azure"` | |
| 147 | +| vSphere | `VspherePlatform` | `"Platform:vsphere"` | |
| 148 | + |
| 149 | +## Suite Types |
| 150 | + |
| 151 | +| Type | Describe Annotation | |
| 152 | +|---|---| |
| 153 | +| longduration | `[sig-mco][Suite:openshift/machine-config-operator/longduration][Serial][Disruptive]` | |
| 154 | +| disruptive | `[sig-mco][Suite:openshift/machine-config-operator/disruptive][Serial][Disruptive]` | |
| 155 | + |
| 156 | +## Path Mappings |
| 157 | + |
| 158 | +| What | Path | |
| 159 | +|---|---| |
| 160 | +| Test files | `test/extended-priv/mco_<feature>.go` | |
| 161 | +| Testdata/templates | `test/extended-priv/testdata/files/` | |
| 162 | +| Utility functions | `test/extended-priv/util/` | |
| 163 | +| Resource wrappers | `test/extended-priv/*.go` | |
| 164 | + |
| 165 | +## Build & Verify |
| 166 | + |
| 167 | +```bash |
| 168 | +make machine-config-tests-ext |
| 169 | +./_output/linux/amd64/machine-config-tests-ext list | grep <PolarionID> |
| 170 | +``` |
0 commit comments