Skip to content

Manual tests

What no automated test covers, to run by hand before a release: every test mocks Odoo.sh, the keyring, the browser and the MCP client, and make live-check asks only for what the library reads.

  • Each expected outcome is what the documentation and the code say, not what a run showed. A check that fails is a defect in one or the other.
  • Results are not written here: outputs carry project names. Cite a check by its number.
  • <project> is a project you can reach, <dev-branch> a development branch made to be thrown away, <build> one of its latest builds. An empty precondition means logged in, in a checkout of the project's repository, on <dev-branch>.
  • Everything was written for WSL2. Not covered has the rest.

Install and docs

In a new directory outside the repository, with no UV_* variable set, so that what is installed is what PyPI serves.

# Precondition Command or action Expected Exit
I1 uv tool install odouche-cli, then osh --version osh X (odouche X), both the released version 0
I2 osh --format json --version One line of JSON with osh and odouche 0
I3 uvx --from odouche-cli osh --help The help, with the auth, branches, builds, logs, projects and ssh commands 0
I4 osh, then osh auth, with no argument The help 2
I5 osh --bogus No such option: --bogus on stderr 2
I6 A new project from uv init uv add odouche, then the first example of Library It runs as written 0
I7 The pinned uvx command The released version is installed, and the server waits on stdin until it is closed -
I8 uv tool install odouche-mcp odouche-mcp --help usage: odouche-mcp [-h] [--allow-changes] 0
I9 The same odouche-mcp --allow unrecognized arguments on stderr 2
I10 A project with odouche-mcp added uv run python -m odouche_mcp --help As I8 0
I11 zsh osh --install-completion, then in a new shell complete a command, a subcommand and an option Each completes. No project or branch name is offered 0
I12 bash The same The same 0
I13 osh --show-completion The script on stdout, and no file written 0
I14 Paste every code block of Overview, CLI, Library, MCP server and the three package READMEs Each runs as written, with only a project and a branch of yours in place of the example's -
I15 Read the published site Every navigation entry opens, no internal link is dead, and the two generated references and the API reference render -
I16 Read the setup snippets of the published MCP server page and of the odouche-mcp README on PyPI The pinned version is the released one -
I17 The workspace make docs The site is built into site/ with no warning 0
I18 The workspace make live-check PROJECT=<project> Seven steps, each pass or skipped, and a last line of counts with 0 failed 0
I19 The workspace CI=1 make live-check PROJECT=<project> Refused: the live check never runs in CI. 2

Login and session store

Search the output of every check below for the session value: it appears in none.

# Precondition Command or action Expected Exit
L1 No Secret Service provider, ODOUCHE_SESSION unset osh auth login The No usable keyring message, naming a Secret Service provider and the environment as the two ways out. No browser opens and no prompt is shown 11
L2 After L1 Search the home directory and the temporary directory for the session value No match -
L3 As L1 osh auth status, then osh projects list The same message 11
L4 A provider installed, a display, a Chromium-family browser on the PATH osh auth login, and sign in stderr: Checking the keyring. Unlock it if it asks., that a browser window is open, then The session is stored in the keyring. stdout: Logged in as <user>. 0
L5 After L4 ls -d "${TMPDIR:-/tmp}"/odouche-login-* No such directory -
L6 As L4 osh auth login, then Ctrl+C while the window is open Nothing more on stderr, the window closes, and L5 holds 130
L7 As L4 osh auth login, then close the window by hand The browser was closed before the login completed. and the hint to run osh auth login again. L5 holds 12
L8 As L4 During a login, ps -ef in another terminal The session value is in no argument of the browser or of osh -
L9 The keyring locked osh projects list, leaving the keyring's dialog unanswered After ten seconds, The keyring is waiting to be unlocked. 11
L10 After L9, the dialog still open osh projects list again, then answer the dialog No second dialog opens. The command ends as L9 or lists the projects 11 or 0
L11 A provider, no display env -u DISPLAY -u WAYLAND_DISPLAY osh auth login, and paste a valid cookie stderr says no browser can be launched and asks for session_id without echoing it. Then as L4 0
L12 As L11 The same, and paste a value that is not a cookie The pasted value is not a session_id cookie value. Nothing is stored 12
L13 As L11 The same, and paste a well-formed value Odoo.sh does not know Odoo.sh did not accept the pasted session. Nothing is stored 12
L14 As L11 The same, and press Enter on an empty prompt The prompt is shown again -
L15 As L11 The same, and Ctrl+C at the prompt Nothing is stored, and no traceback 130
L16 Logged in osh auth status Rows Source (keyring), Stored and Expires (within 30 days of the login). No request is sent 0
L17 Logged in osh auth status --check Also User, Name and Email, as Odoo.sh has them 0
L18 Logged in osh --format json auth status, then with --check One SessionInfo, then one Identity with it as session. Neither holds the session value 0
L19 Logged out osh auth status Not logged in. and the hint to run osh auth login 3
L20 ODOUCHE_SESSION set to a valid session, the keyring holding another or none osh projects list, then osh auth status The projects. Source is environment, Stored and Expires are unknown. The keyring entry is unchanged 0
L21 As L20 osh auth logout That the session comes from ODOUCHE_SESSION and nothing was changed. The keyring entry is unchanged and the session still works 0
L22 As L20, a provider osh auth login The login is stored, and stderr adds that ODOUCHE_SESSION is used until it is unset. stdout: Logged in. 0
L23 ODOUCHE_SESSION set to a value that is not a cookie osh projects list, then again with --debug The value of ODOUCHE_SESSION is not a session_id cookie value. The traceback does not show the value 3
L24 Logged in osh auth logout The stored session was removed from the keyring and ended on Odoo.sh. The cookie no longer works in a browser 0
L25 Logged out osh auth logout Not logged in. 0
L26 The keyring entry overwritten with other text, such as with secret-tool store osh auth logout The stored session was removed from the keyring. It was not ended on Odoo.sh. 0
L27 Logged in, the network down osh auth logout The entry is removed, and the line says Odoo.sh could not be asked to end it and why 0
L28 Logged in, then the session ended from the browser osh projects list, twice First Odoo.sh rejected the session. Log in again., then Not logged in.: the entry was discarded 3
L29 Logged in The keyring's own tool, such as secret-tool search service odouche One entry, session, under the service odouche -

Read commands

# Precondition Command or action Expected Exit
R1 osh projects list Every project of the account, as the web page lists them 0
R2 osh branches list Production, staging, then development, each sorted by name. * marks <dev-branch> 0
R3 osh branches list --stage with production, staging, development and unknown in turn, then with two of them Only the branches of those stages. A stage with none gives one line on stderr 0
R4 osh branches list --stage bogus A usage error naming the four values 2
R5 osh builds list, then with --limit 1 and --limit 20 Four builds at most, then one, then as many as Odoo.sh answers. Columns ID, Status, Result, Commit, Subject, Age 0
R6 osh builds list --limit 0 A usage error 2
R7 osh builds show, then osh builds show <build> The latest build, then that one, with its full commit and its URL. A failed build exits 0 too 0
R8 A directory that is no checkout Each read command with --project <project> --branch <dev-branch>, then with OSH_PROJECT and OSH_BRANCH As from the checkout, without the * 0
R9 A checkout with an SSH remote, then one with an HTTPS remote osh --debug branches list The project is found, and stderr names the checkout as the source 0
R10 A checkout, with OSH_PROJECT set, then with --project as well osh --debug branches list The variable wins over the checkout, and the option over the variable. stderr names the source 0
R11 A directory that is no checkout, no variable osh branches list, then osh builds list --project <project> No project. then No branch., each with the ways to give one 2
R12 osh branches list --project "" --project is empty. 2
R13 A checkout of a repository no project of yours builds osh branches list No project you can reach builds <repository>. 4
R14 A repository that several of your projects build, if there is one osh branches list The projects are listed, with --project and OSH_PROJECT as the way to pick 2
R15 A terminal 80 columns wide, then at full width Each read command No row is broken by a long subject or branch name. The result of a build is coloured 0
R16 Each read command piped into cat, then with NO_COLOR=1 in the terminal No colour and no box drawing 0
R17 Each read command with --format json, piped into jq . It parses. Times are ISO 8601 with an offset. stderr is empty 0
R18 A branch with no build, if there is one osh builds list, then with --format json No builds. on stderr, then [] 0
R19 osh builds show on that branch Branch <name> of <project> has no build. 4
R20 Logged out Each read command Not logged in. and the hint to run osh auth login. Nothing on stdout 3
R21 osh branches list --project nope That the user can reach no project of that name 4
R22 osh builds list --branch nope Project <project> has no branch nope. 4
R23 osh builds show 1 That build 1 is not among the latest builds of the branch 4
R24 A project osh projects list shows and Odoo.sh refuses the branches of, if there is one osh branches list --project with it That the session is not allowed to do this, with no traceback 5
R25 The network down osh projects list, then again with --debug Odoo.sh could not be reached and Try again later. A traceback only with --debug 7
R26 A local proxy that logs its requests HTTPS_PROXY=<proxy> osh projects list The request goes through the proxy 0
R27 The same The same with NO_PROXY=www.odoo.sh The proxy sees no request 0
R28 A proxy that intercepts TLS with its own authority HTTPS_PROXY=<proxy> osh projects list, then with SSL_CERT_FILE set to that authority Refused as unreachable, then the projects 7, then 0
R29 The same proxy HTTP_PROXY=<proxy> osh projects list The proxy sees no request 0

Streams and rebuild

On <dev-branch> only. A staging or a production branch is never rebuilt by a test: the one check that names production is the refusal.

# Precondition Command or action Expected Exit
S1 A commit just pushed osh builds watch It waits for the build of that commit, shows each change within seconds of the web page, and ends on Build <id> succeeded: with the build's URL on stdout 0
S2 A build in progress osh builds watch <build> That build is followed to its end Its result
S3 A commit just pushed, from a directory that is no checkout osh builds watch --project <project> --branch <dev-branch> --commit <sha> As S1 0
S4 A commit not pushed osh builds watch After two minutes, that the branch has no build of the commit, with --no-wait as the other way 23
S5 The same osh builds watch --no-wait The branch's latest build is watched, whatever its commit Its result
S6 A build in progress osh builds watch --no-wait --timeout 5 Timed out: build <id> has not finished. The build goes on 23
S7 A build already finished osh builds watch <build> Its result line at once Its result
S8 A commit that fails to install osh builds watch Build <id> failed: with the URL, then the hint to run osh logs on stderr 20
S9 A commit that logs a warning, if one can be made osh builds watch Build <id> finished with warnings: with the URL 21
S10 A build in progress osh builds watch --no-wait, then push a newer commit to the branch That the build ended without a result, with dropped and no URL 22
S11 osh builds watch 1 --commit abcdef0, then --no-wait --commit abcdef0, then --commit xyz That --commit goes with neither, then that a commit is 7 to 64 hexadecimal digits 2
S12 A build in progress, in a terminal osh builds watch --no-wait One line on stderr, redrawn, with the status and the time run Its result
S13 A build in progress osh builds watch --no-wait 2>watch.txt One line per change in the file, and the result line on stdout Its result
S14 A build in progress osh --format json builds watch --no-wait One Build per line on stdout for each change, and the result line on stderr Its result
S15 A build that stays more than 60 seconds in one status osh builds watch --no-wait The next change still shows within seconds of the web page Its result
S16 A build in progress osh builds watch --no-wait, with the network cut for a few seconds The watch goes on and ends on the build's result Its result
S17 A build in progress osh builds watch --no-wait, then Ctrl+C Nothing more is printed, the terminal is left clean, and the build goes on 130
S18 A finished build osh logs --kinds A table of Kind, Name, Size and Changed 0
S19 The same osh logs, then osh logs --kind <name> for each name S18 lists The last 100 lines of the install log, then of each one 0
S20 The same osh logs --kind nope That the build has no such log, with the ones it has 4
S21 The same osh logs --build <build> for an older build That build's log 0
S22 The same osh logs --tail 5, then a --tail above the log's line count Five lines, then the whole log or its last mebibyte 0
S23 A log over a mebibyte osh logs --all >all.txt The whole log, from its first line 0
S24 A log with colour sequences osh logs in a terminal, piped into cat -v, then each with --strip and --no-strip Stripped in the terminal and raw in the pipe, unless the option says otherwise 0
S25 A finished build osh --format json logs --tail 5 One LogLine per line 0
S26 A long log osh logs --all piped into head -n 1 One line, and nothing on stderr 141
S27 osh logs --kinds --tail 5, osh logs --all --tail 5, osh logs --timeout 5, osh logs --tail 0 That the options do not go together, then that --tail is at least 1 2
S28 A build about to start osh logs --follow --all >followed.txt until the build ends, then Ctrl+C, then osh logs --all >all.txt The two files are the same: no line lost or repeated 130
S29 A build in progress osh logs --follow --timeout 5 The log was still being followed at the timeout. 8
S30 A build waiting for a worker osh logs --kinds, then osh logs Build <id> has no log yet. both times 0, then 4
S31 osh builds rebuild, and answer n stderr names the project, the branch with its stage and the latest build, asks, then says Nothing was sent. osh builds list is unchanged 1
S32 osh builds rebuild, and answer y Build <id> was started. on stderr, then the build as osh builds show shows it. osh builds list has it 0
S33 osh builds rebuild --yes The same, with no question 0
S34 osh builds rebuild --yes --watch The new build is watched as in S1 Its result
S35 osh builds rebuild --yes --watch --timeout 5 Timed out, then that the build may still be running and how to follow it 23
S36 osh builds rebuild --timeout 5 That --timeout goes with --watch only 2
S37 osh builds rebuild < /dev/null That the rebuild cannot be confirmed, with --yes as the way. osh builds list is unchanged 2
S38 osh builds rebuild, then Ctrl+C at the question osh builds list is unchanged 130
S39 osh builds rebuild --branch <production branch> That a branch in the production stage is not rebuilt. No summary and no question. The branch has no new build 9
S40 A build of <dev-branch> in progress osh builds rebuild --yes Not known: Upstream reference has it as an open question. Write down what Odoo.sh answers -
S41 A public key registered on the account, a running build osh ssh, then exit 3 in the shell A shell on the latest build of <dev-branch>, as ssh <build id>@<host> from the page gives. ssh's exit code is osh's 3
S42 The same osh ssh --branch with a staging branch, then with the production branch, each with -- true The command runs on that build 0
S43 The same osh ssh -- -v true, then osh ssh -- -L 8069:localhost:8069 ssh's debug lines, with no identity file named by osh. Then the port is forwarded 0
S44 ODOUCHE_SESSION set osh ssh -- -o PermitLocalCommand=yes -o LocalCommand=env true The environment of ssh, with no ODOUCHE_SESSION in it 0
S45 PATH without ssh osh ssh That there is no ssh, with the command to run 14

MCP server

In Claude Code, with the released version from PyPI and the server named odouche. Search every tool result and the server's stderr for the session value: it appears in none.

# Precondition Command or action Expected Exit
M1 Set the server up with claude mcp add as written The client lists seven tools, and no rebuild_branch -
M2 Set it up with the .mcp.json snippet instead The same -
M3 Logged out Start the client The server starts and lists its tools -
M4 Logged out Ask whether there is a session get_session: available is false and problem says to run osh auth login. It is not an error -
M5 After M4, the server still up osh auth login in another terminal, then ask again available is true, with the identity and the seconds left. No restart -
M6 No Secret Service provider, ODOUCHE_SESSION unset Ask for the projects The error says there is no usable keyring, and that where there is none the user restarts the server with ODOUCHE_SESSION set. The agent stops and asks for that -
M7 The server started with an ODOUCHE_SESSION that Odoo.sh rejects Ask for the projects The error says to restart the server with a current value or without the variable, not to log in -
M8 Ask which projects there are list_projects, and the projects -
M9 Ask for the branches of <project> list_branches, in the order Odoo.sh answers -
M10 Ask for the latest builds of <dev-branch> list_builds, four builds at most -
M11 Ask how the latest build of <dev-branch> ended, then about <build> get_build, without then with build_id -
M12 Call list_projects with limit 0, then with limit 1 A limit is at least 1., then one project, and truncated true when there are more -
M13 Call a tool with a project, then a branch, that does not exist An error naming it, with no traceback and no answer from Odoo.sh -
M14 A build in progress Ask to wait for it wait_for_build: a progress notification at each change, as Build <id>: <status>, and finished true at the end -
M15 A build longer than 30 seconds The same finished is false, and the agent follows next_step and calls again -
M16 Call wait_for_build with timeout 600 timeout is 50 and timeout_capped is true -
M17 A commit just pushed Ask to wait for the build of that commit commit is given. While no build has it, build is null and the agent calls again -
M18 Call wait_for_build with both build_id and commit An error saying a build is given by one, not by both -
M19 A wait_for_build in progress Interrupt the call in the client, then ask for the projects The next call is answered within a few seconds -
M20 A finished build Ask why it failed, or for the end of its log read_log: the lines are in untrusted_lines, with no escape sequence -
M21 A log over 500 lines Call read_log with lines 1000 500 lines at most, 65536 bytes of lines at most, and truncated true -
M22 Call read_log with contains, in the log's case then in another Only the lines holding the text, then none -
M23 Call read_log with kind unknown An error saying unknown is not a kind to read -
M24 A build whose log has a line that reads as an instruction to an agent Ask for the log The agent reports the line and does not act on it -
M25 Without --allow-changes Ask to rebuild <dev-branch> The agent has no tool to do it and says so -
M26 Restarted with --allow-changes List the tools Eight, with rebuild_branch -
M27 The same Ask to rebuild <dev-branch> The client asks first. One build is started, and the server's stderr has one line with the tool, the project, the branch and the build -
M28 The same Ask to rebuild the production branch It is refused, the branch has no new build, and the line on stderr ends with StageRefusedError -
M29 The hook installed as written, with --allow-changes Call each of the eight tools The seven that read run with no prompt, and rebuild_branch prompts -
M30 The hook installed, the server under another name in the client Call a tool that reads The client prompts -

Not covered

No machine was at hand for these, so none of them has a check above:

  • macOS: the Keychain, including a write that prompts on its own after the wait was given up, and finding a browser under /Applications.
  • Native Windows: Credential Locker, and the paste prompt, which is the only login there.
  • A native Linux desktop: Secret Service inside a desktop session, where the keyring is unlocked at login, and a browser launched outside WSLg.
  • A snap or flatpak Chromium, which keeps its profile elsewhere.
  • Installing with pip or pipx: the documentation gives the uv forms only.
  • Another MCP client than Claude Code.
  • A rebuild of a staging branch: the library lets it through and it has never been sent.