99 "github.com/aixgo-dev/code/internal/domain"
1010 "github.com/aixgo-dev/code/internal/engine/claude"
1111 "github.com/aixgo-dev/code/internal/engine/codex"
12+ "github.com/aixgo-dev/code/internal/engine/gemini"
1213 forge2 "github.com/aixgo-dev/code/internal/forge"
1314 "github.com/aixgo-dev/code/internal/forge/dryrun"
1415 forgefake "github.com/aixgo-dev/code/internal/forge/fake"
@@ -462,6 +463,25 @@ func TestEngineEnvSkipsAzureForClaude(t *testing.T) {
462463 }
463464}
464465
466+ func TestEngineEnvRequiresVertexForGemini (t * testing.T ) {
467+ t .Setenv ("AIXGO_AZURE_OPENAI_ENDPOINT" , "" )
468+ t .Setenv ("AIXGO_AZURE_OPENAI_API_KEY" , "" )
469+ t .Setenv ("AIXGO_VERTEX_PROJECT" , "" )
470+ t .Setenv ("AIXGO_VERTEX_API_KEY" , "" )
471+ if _ , err := engineEnv (& config.Config {Engine : "gemini" }); err == nil || ! strings .Contains (err .Error (), "AIXGO_VERTEX_PROJECT" ) {
472+ t .Fatal ("gemini without a project must name AIXGO_VERTEX_PROJECT" )
473+ }
474+ t .Setenv ("AIXGO_VERTEX_PROJECT" , "simplycubed-agents" )
475+ if _ , err := engineEnv (& config.Config {Engine : "gemini" }); err == nil || ! strings .Contains (err .Error (), "AIXGO_VERTEX_API_KEY" ) {
476+ t .Fatal ("gemini without a key must name AIXGO_VERTEX_API_KEY" )
477+ }
478+ t .Setenv ("AIXGO_VERTEX_API_KEY" , "k" )
479+ got , err := engineEnv (& config.Config {Engine : "gemini" })
480+ if err != nil || got != "" {
481+ t .Fatalf ("engineEnv(gemini) = %q, %v" , got , err )
482+ }
483+ }
484+
465485func TestPreflightCmd (t * testing.T ) {
466486 t .Run ("reports ok when config and engine settings are present" , func (t * testing.T ) {
467487 t .Setenv ("AIXGO_AZURE_OPENAI_ENDPOINT" , "https://r.openai.azure.com" )
@@ -487,6 +507,20 @@ func TestPreflightCmd(t *testing.T) {
487507 }
488508 })
489509
510+ t .Run ("allows gemini with Vertex settings and no Azure" , func (t * testing.T ) {
511+ t .Setenv ("AIXGO_AZURE_OPENAI_ENDPOINT" , "" )
512+ t .Setenv ("AIXGO_AZURE_OPENAI_API_KEY" , "" )
513+ t .Setenv ("AIXGO_VERTEX_PROJECT" , "simplycubed-agents" )
514+ t .Setenv ("AIXGO_VERTEX_API_KEY" , "k" )
515+ var out bytes.Buffer
516+ if err := preflightCmd ([]string {"--repo-dir" , repoWithConfigBody (t , "gate: make check\n engine: gemini\n appName: acme-code\n " )}, & out ); err != nil {
517+ t .Fatalf ("unexpected error: %v" , err )
518+ }
519+ if ! strings .Contains (out .String (), "preflight ok" ) {
520+ t .Fatalf ("output = %q" , out .String ())
521+ }
522+ })
523+
490524 // The whole point of preflight is naming what is wrong, so each failure
491525 // asserts the message identifies the thing the operator has to fix.
492526 t .Run ("names the missing config" , func (t * testing.T ) {
@@ -673,6 +707,24 @@ func TestPrepare(t *testing.T) {
673707 }
674708 })
675709
710+ t .Run ("allows gemini with Vertex settings and skips codex config" , func (t * testing.T ) {
711+ t .Setenv ("AIXGO_AZURE_OPENAI_ENDPOINT" , "" )
712+ t .Setenv ("AIXGO_AZURE_OPENAI_API_KEY" , "" )
713+ t .Setenv ("AIXGO_VERTEX_PROJECT" , "simplycubed-agents" )
714+ t .Setenv ("AIXGO_VERTEX_API_KEY" , "k" )
715+ stateDir := t .TempDir ()
716+ c , _ , err := prepare ("run" , []string {"--repo-dir" , repoWithConfigBody (t , "gate: make check\n engine: gemini\n appName: acme-code\n " ), "--state-dir" , stateDir })
717+ if err != nil {
718+ t .Fatalf ("unexpected error: %v" , err )
719+ }
720+ if _ , ok := c .deps .Runner .(* gemini.Runner ); ! ok {
721+ t .Fatalf ("expected the Gemini runner, got %#v" , c .deps .Runner )
722+ }
723+ if _ , err := os .Stat (filepath .Join (stateDir , "codex-home" , "config.toml" )); ! errors .Is (err , os .ErrNotExist ) {
724+ t .Fatalf ("gemini runs must not render a codex config, stat err = %v" , err )
725+ }
726+ })
727+
676728 t .Run ("rejects an unknown flag" , func (t * testing.T ) {
677729 if _ , _ , err := prepare ("run" , []string {"--nope" }); err == nil {
678730 t .Fatal ("expected an error for an unknown flag" )
@@ -1017,15 +1069,30 @@ func TestCommandCmdRejectsABodyFlagWithNoValue(t *testing.T) {
10171069// Engine selection is a documented config key and had no test. The default
10181070// matters most: an existing repository must keep the engine it already had.
10191071func TestNewRunnerSelectsTheEngine (t * testing.T ) {
1020- if _ , ok := newRunner (& config.Config {}, t .TempDir ()).(* codex.Runner ); ! ok {
1072+ if _ , ok := newRunner (& config.Config {}, t .TempDir (), "" ).(* codex.Runner ); ! ok {
10211073 t .Fatal ("the default engine must be codex" )
10221074 }
1023- if _ , ok := newRunner (& config.Config {Engine : "claude" }, t .TempDir ()).(* claude.Runner ); ! ok {
1075+ if _ , ok := newRunner (& config.Config {Engine : "claude" }, t .TempDir (), "" ).(* claude.Runner ); ! ok {
10241076 t .Fatal ("engine: claude must select the Claude adapter" )
10251077 }
1078+ g , ok := newRunner (& config.Config {Engine : "gemini" }, t .TempDir (), "" ).(* gemini.Runner )
1079+ if ! ok {
1080+ t .Fatal ("engine: gemini must select the Gemini adapter" )
1081+ }
1082+ if g .Model != "gemini-2.5-pro" {
1083+ t .Fatalf ("gemini default model = %q, want gemini-2.5-pro" , g .Model )
1084+ }
1085+ g2 , _ := newRunner (& config.Config {Engine : "gemini" }, t .TempDir (), "gpt-5.4" ).(* gemini.Runner )
1086+ if g2 .Model != "gemini-2.5-pro" {
1087+ t .Fatalf ("the Codex default must not be forwarded to Gemini: %q" , g2 .Model )
1088+ }
1089+ g3 , _ := newRunner (& config.Config {Engine : "gemini" }, t .TempDir (), "gemini-2.5-flash" ).(* gemini.Runner )
1090+ if g3 .Model != "gemini-2.5-flash" {
1091+ t .Fatalf ("an explicit gemini model must be kept: %q" , g3 .Model )
1092+ }
10261093 // An unrecognised engine falls back rather than failing later with an
10271094 // error that says nothing about the cause.
1028- if _ , ok := newRunner (& config.Config {Engine : "nope" }, t .TempDir ()).(* codex.Runner ); ! ok {
1095+ if _ , ok := newRunner (& config.Config {Engine : "nope" }, t .TempDir (), "" ).(* codex.Runner ); ! ok {
10291096 t .Fatal ("an unknown engine must fall back to the default" )
10301097 }
10311098}
@@ -1034,15 +1101,15 @@ func TestNewRunnerSelectsTheEngine(t *testing.T) {
10341101// widen it themselves. Nothing in this repository sets it, so the test asserts
10351102// both that the default is untouched and that an explicit value is honoured.
10361103func TestNewRunnerHonoursTheSandboxOverride (t * testing.T ) {
1037- r , ok := newRunner (& config.Config {}, t .TempDir ()).(* codex.Runner )
1104+ r , ok := newRunner (& config.Config {}, t .TempDir (), "" ).(* codex.Runner )
10381105 if ! ok {
10391106 t .Fatal ("expected the codex runner" )
10401107 }
10411108 if r .Sandbox != "workspace-write" {
10421109 t .Fatalf ("the sandbox must stay on by default, got %q" , r .Sandbox )
10431110 }
10441111 t .Setenv ("AIXGO_SANDBOX" , "read-only" )
1045- r2 , _ := newRunner (& config.Config {}, t .TempDir ()).(* codex.Runner )
1112+ r2 , _ := newRunner (& config.Config {}, t .TempDir (), "" ).(* codex.Runner )
10461113 if r2 .Sandbox != "read-only" {
10471114 t .Fatalf ("an explicit sandbox mode must be honoured, got %q" , r2 .Sandbox )
10481115 }
0 commit comments