Files
chatgpt-cli/integration/integration_test.go
2024-08-17 11:42:31 -04:00

804 lines
26 KiB
Go

package integration_test
import (
"fmt"
"github.com/kardolus/chatgpt-cli/config"
"github.com/kardolus/chatgpt-cli/configmanager"
"github.com/kardolus/chatgpt-cli/history"
"github.com/kardolus/chatgpt-cli/types"
"github.com/kardolus/chatgpt-cli/utils"
"github.com/onsi/gomega/gexec"
"github.com/sclevine/spec"
"github.com/sclevine/spec/report"
"io"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
"sync"
"testing"
"time"
. "github.com/onsi/gomega"
)
const (
gitCommit = "some-git-commit"
gitVersion = "some-git-version"
servicePort = ":8080"
serviceURL = "http://0.0.0.0" + servicePort
)
var (
once sync.Once
)
func TestIntegration(t *testing.T) {
defer gexec.CleanupBuildArtifacts()
spec.Run(t, "Integration Tests", testIntegration, spec.Report(report.Terminal{}))
}
func testIntegration(t *testing.T, when spec.G, it spec.S) {
it.Before(func() {
RegisterTestingT(t)
})
when("Read and Write History", func() {
const threadName = "default-thread"
var (
tmpDir string
fileIO *history.FileIO
messages []types.Message
err error
)
it.Before(func() {
tmpDir, err = os.MkdirTemp("", "chatgpt-cli-test")
Expect(err).NotTo(HaveOccurred())
fileIO, _ = history.New()
fileIO = fileIO.WithDirectory(tmpDir)
fileIO.SetThread(threadName)
messages = []types.Message{
{
Role: "user",
Content: "Test message 1",
},
{
Role: "assistant",
Content: "Test message 2",
},
}
})
it.After(func() {
Expect(os.RemoveAll(tmpDir)).To(Succeed())
})
it("writes the messages to the file", func() {
err = fileIO.Write(messages)
Expect(err).NotTo(HaveOccurred())
})
it("reads the messages from the file", func() {
err = fileIO.Write(messages) // need to write before reading
Expect(err).NotTo(HaveOccurred())
readMessages, err := fileIO.Read()
Expect(err).NotTo(HaveOccurred())
Expect(readMessages).To(Equal(messages))
})
})
when("Read, Write, List, Delete Config", func() {
var (
tmpDir string
tmpFile *os.File
historyDir string
configIO *config.FileIO
testConfig types.Config
err error
)
it.Before(func() {
tmpDir, err = os.MkdirTemp("", "chatgpt-cli-test")
Expect(err).NotTo(HaveOccurred())
historyDir, err = os.MkdirTemp(tmpDir, "history")
Expect(err).NotTo(HaveOccurred())
tmpFile, err = os.CreateTemp(tmpDir, "config.yaml")
Expect(err).NotTo(HaveOccurred())
Expect(tmpFile.Close()).To(Succeed())
configIO = config.New().WithConfigPath(tmpFile.Name()).WithHistoryPath(historyDir)
testConfig = types.Config{
Model: "test-model",
}
})
it.After(func() {
Expect(os.RemoveAll(tmpDir)).To(Succeed())
})
it("writes the config to the file", func() {
err = configIO.Write(testConfig)
Expect(err).NotTo(HaveOccurred())
})
when("reading the config from a file", func() {
defaults := config.New().ReadDefaults()
it("it doesn't apply a migration when max_tokens is 0", func() {
testConfig.MaxTokens = 0
err = configIO.Write(testConfig) // need to write before reading
Expect(err).NotTo(HaveOccurred())
readConfig, err := configIO.Read()
Expect(err).NotTo(HaveOccurred())
Expect(readConfig).To(Equal(testConfig))
})
it("it migrates small values of max_tokens as expected", func() {
testConfig.MaxTokens = defaults.ContextWindow - 1
err = configIO.Write(testConfig) // need to write before reading
Expect(err).NotTo(HaveOccurred())
readConfig, err := configIO.Read()
Expect(err).NotTo(HaveOccurred())
expectedConfig := testConfig
expectedConfig.MaxTokens = defaults.MaxTokens
expectedConfig.ContextWindow = defaults.ContextWindow
Expect(readConfig).To(Equal(expectedConfig))
})
it("it migrates large values of max_tokens as expected", func() {
testConfig.MaxTokens = defaults.ContextWindow + 1
err = configIO.Write(testConfig) // need to write before reading
Expect(err).NotTo(HaveOccurred())
readConfig, err := configIO.Read()
Expect(err).NotTo(HaveOccurred())
expectedConfig := testConfig
expectedConfig.MaxTokens = defaults.MaxTokens
expectedConfig.ContextWindow = testConfig.MaxTokens
Expect(readConfig).To(Equal(expectedConfig))
})
it("preserves comments when writing the config to the file", func() {
configFile := filepath.Join(tmpDir, "config_with_comments.yaml")
err := os.WriteFile(configFile, []byte(`
# This is a model configuration
model: gpt-3.5-turbo # Default model
# Maximum number of tokens
max_tokens: 100
`), 0644)
Expect(err).NotTo(HaveOccurred())
configIO = config.New().WithConfigPath(configFile)
// Read the existing configuration, modify a value, and write it back
readConfig, err := configIO.Read()
Expect(err).NotTo(HaveOccurred())
// Modify the configuration
readConfig.Model = "new-model"
err = configIO.Write(readConfig)
Expect(err).NotTo(HaveOccurred())
// Read the file content back
content, err := os.ReadFile(configFile)
Expect(err).NotTo(HaveOccurred())
// Check if the comments are still present
Expect(string(content)).To(ContainSubstring("# This is a model configuration"))
Expect(string(content)).To(ContainSubstring("# Default model"))
Expect(string(content)).To(ContainSubstring("# Maximum number of tokens"))
// Verify the configuration was updated
Expect(string(content)).To(ContainSubstring("model: new-model"))
})
})
it("lists all the threads", func() {
files := []string{"thread1.json", "thread2.json", "thread3.json"}
for _, file := range files {
file, err := os.Create(filepath.Join(historyDir, file))
Expect(err).NotTo(HaveOccurred())
Expect(file.Close()).To(Succeed())
}
result, err := configIO.List()
Expect(err).NotTo(HaveOccurred())
Expect(result).To(HaveLen(3))
Expect(result[2]).To(Equal("thread3.json"))
})
it("deletes the thread", func() {
files := []string{"thread1.json", "thread2.json", "thread3.json"}
for _, file := range files {
file, err := os.Create(filepath.Join(historyDir, file))
Expect(err).NotTo(HaveOccurred())
Expect(file.Close()).To(Succeed())
}
err = configIO.Delete("thread2")
Expect(err).NotTo(HaveOccurred())
_, err = os.Stat(filepath.Join(historyDir, "thread2.json"))
Expect(os.IsNotExist(err)).To(BeTrue())
_, err = os.Stat(filepath.Join(historyDir, "thread3.json"))
Expect(os.IsNotExist(err)).To(BeFalse())
})
// Since we don't have a Delete method in the config, we will test if we can overwrite the configuration.
it("overwrites the existing config", func() {
newConfig := types.Config{
Model: "new-model",
}
err = configIO.Write(newConfig)
Expect(err).NotTo(HaveOccurred())
readConfig, err := configIO.Read()
Expect(err).NotTo(HaveOccurred())
Expect(readConfig).To(Equal(newConfig))
})
})
when("Performing the Lifecycle", func() {
const (
exitSuccess = 0
exitFailure = 1
)
var (
homeDir string
filePath string
configFile string
err error
apiKeyEnvVar string
)
runCommand := func(args ...string) string {
command := exec.Command(binaryPath, args...)
session, err := gexec.Start(command, io.Discard, io.Discard)
ExpectWithOffset(1, err).NotTo(HaveOccurred())
<-session.Exited
ExpectWithOffset(1, session).Should(gexec.Exit(0))
return string(session.Out.Contents())
}
checkConfigFileContent := func(expectedContent string) {
content, err := os.ReadFile(configFile)
ExpectWithOffset(1, err).NotTo(HaveOccurred())
ExpectWithOffset(1, string(content)).To(ContainSubstring(expectedContent))
}
it.Before(func() {
once.Do(func() {
SetDefaultEventuallyTimeout(10 * time.Second)
log.Println("Building binary...")
Expect(buildBinary()).To(Succeed())
log.Println("Binary built successfully!")
log.Println("Starting mock server...")
Expect(runMockServer()).To(Succeed())
log.Println("Mock server started!")
Eventually(func() (string, error) {
return curl(fmt.Sprintf("%s/ping", serviceURL))
}).Should(ContainSubstring("pong"))
})
homeDir, err = os.MkdirTemp("", "mockHome")
Expect(err).NotTo(HaveOccurred())
apiKeyEnvVar = configmanager.New(config.New()).WithEnvironment().APIKeyEnvVarName()
Expect(os.Setenv("HOME", homeDir)).To(Succeed())
Expect(os.Setenv(apiKeyEnvVar, expectedToken)).To(Succeed())
})
it.After(func() {
gexec.Kill()
Expect(os.RemoveAll(homeDir))
})
it("throws an error when the API key is missing", func() {
Expect(os.Unsetenv(apiKeyEnvVar)).To(Succeed())
command := exec.Command(binaryPath, "some prompt")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(exitFailure))
output := string(session.Err.Contents())
Expect(output).To(ContainSubstring(apiKeyEnvVar))
})
it("should not require an API key for the --version flag", func() {
Expect(os.Unsetenv(apiKeyEnvVar)).To(Succeed())
command := exec.Command(binaryPath, "--version")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(exitSuccess))
})
it("should require a hidden folder for the --list-threads flag", func() {
command := exec.Command(binaryPath, "--list-threads")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(exitFailure))
output := string(session.Err.Contents())
Expect(output).To(ContainSubstring(".chatgpt-cli/history: no such file or directory"))
})
it("should require an argument for the --set-model flag", func() {
command := exec.Command(binaryPath, "--set-model")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(exitFailure))
output := string(session.Err.Contents())
Expect(output).To(ContainSubstring("flag needs an argument: --set-model"))
})
it("should require an argument for the --set-thread flag", func() {
command := exec.Command(binaryPath, "--set-thread")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(exitFailure))
output := string(session.Err.Contents())
Expect(output).To(ContainSubstring("flag needs an argument: --set-thread"))
})
it("should require an argument for the --set-max-tokens flag", func() {
command := exec.Command(binaryPath, "--set-max-tokens")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(exitFailure))
output := string(session.Err.Contents())
Expect(output).To(ContainSubstring("flag needs an argument: --set-max-tokens"))
})
it("should require an argument for the --set-context-window flag", func() {
command := exec.Command(binaryPath, "--set-context-window")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(exitFailure))
output := string(session.Err.Contents())
Expect(output).To(ContainSubstring("flag needs an argument: --set-context-window"))
})
it("should require the chatgpt-cli folder but not an API key for the --set-model flag", func() {
Expect(os.Unsetenv(apiKeyEnvVar)).To(Succeed())
command := exec.Command(binaryPath, "--set-model", "123")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(exitFailure))
output := string(session.Err.Contents())
Expect(output).To(ContainSubstring(".chatgpt-cli/config.yaml: no such file or directory"))
Expect(output).NotTo(ContainSubstring(apiKeyEnvVar))
})
it("should require the chatgpt-cli folder but not an API key for the --set-thread flag", func() {
Expect(os.Unsetenv(apiKeyEnvVar)).To(Succeed())
command := exec.Command(binaryPath, "--set-thread", "thread-name")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(exitFailure))
output := string(session.Err.Contents())
Expect(output).To(ContainSubstring(".chatgpt-cli/config.yaml: no such file or directory"))
Expect(output).NotTo(ContainSubstring(apiKeyEnvVar))
})
it("should require the chatgpt-cli folder but not an API key for the --set-max-tokens flag", func() {
Expect(os.Unsetenv(apiKeyEnvVar)).To(Succeed())
command := exec.Command(binaryPath, "--set-max-tokens", "789")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(exitFailure))
output := string(session.Err.Contents())
Expect(output).To(ContainSubstring(".chatgpt-cli/config.yaml: no such file or directory"))
Expect(output).NotTo(ContainSubstring(apiKeyEnvVar))
})
it("should require the chatgpt-cli folder but not an API key for the --set-context-window flag", func() {
Expect(os.Unsetenv(apiKeyEnvVar)).To(Succeed())
command := exec.Command(binaryPath, "--set-context-window", "789")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(exitFailure))
output := string(session.Err.Contents())
Expect(output).To(ContainSubstring(".chatgpt-cli/config.yaml: no such file or directory"))
Expect(output).NotTo(ContainSubstring(apiKeyEnvVar))
})
it("should return the expected result for the --version flag", func() {
output := runCommand("--version")
Expect(output).To(ContainSubstring(fmt.Sprintf("commit %s", gitCommit)))
Expect(output).To(ContainSubstring(fmt.Sprintf("version %s", gitVersion)))
})
it("should return the expected result for the --list-models flag", func() {
output := runCommand("--list-models")
Expect(output).To(ContainSubstring("* gpt-3.5-turbo (current)"))
Expect(output).To(ContainSubstring("- gpt-3.5-turbo-0301"))
})
it("should return the expected result for the --query flag", func() {
Expect(os.Setenv("OPENAI_TRACK_TOKEN_USAGE", "false")).To(Succeed())
output := runCommand("--query", "some-query")
expectedResponse := `I don't have personal opinions about bars, but here are some popular bars in Red Hook, Brooklyn:`
Expect(output).To(ContainSubstring(expectedResponse))
Expect(output).NotTo(ContainSubstring("Token Usage:"))
})
it("should display token usage after a query when configured to do so", func() {
Expect(os.Setenv("OPENAI_TRACK_TOKEN_USAGE", "true")).To(Succeed())
output := runCommand("--query", "tell me a 5 line joke")
Expect(output).To(ContainSubstring("Token Usage:"))
})
it("prints debug information with the --debug flag", func() {
Expect(os.Setenv("OPENAI_DEBUG", "true")).To(Succeed())
output := runCommand("--query", "tell me a joke")
Expect(output).To(ContainSubstring("Generated cURL command"))
Expect(output).To(ContainSubstring("/v1/chat/completions"))
Expect(output).To(ContainSubstring("--header \"Authorization: Bearer ${OPENAI_API_KEY}\""))
Expect(output).To(ContainSubstring("--header 'Content-Type: application/json'"))
Expect(output).To(ContainSubstring("\"model\":\"gpt-3.5-turbo\""))
Expect(output).To(ContainSubstring("\"messages\":"))
Expect(output).To(ContainSubstring("Response"))
Expect(os.Unsetenv("OPENAI_DEBUG")).To(Succeed())
})
it("should assemble http errors as expected", func() {
Expect(os.Setenv(apiKeyEnvVar, "wrong-token")).To(Succeed())
command := exec.Command(binaryPath, "--query", "some-query")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(exitFailure))
output := string(session.Err.Contents())
// see error.json
Expect(output).To(Equal("http status 401: Incorrect API key provided\n"))
})
when("there is a hidden chatgpt-cli folder in the home dir", func() {
it.Before(func() {
filePath = path.Join(os.Getenv("HOME"), ".chatgpt-cli")
Expect(os.MkdirAll(filePath, 0777)).To(Succeed())
})
it.After(func() {
Expect(os.RemoveAll(filePath)).To(Succeed())
})
it("should not require an API key for the --list-threads flag", func() {
historyPath := path.Join(filePath, "history")
Expect(os.MkdirAll(historyPath, 0777)).To(Succeed())
Expect(os.Unsetenv(apiKeyEnvVar)).To(Succeed())
command := exec.Command(binaryPath, "--list-threads")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(exitSuccess))
})
it("migrates the legacy history as expected", func() {
// Legacy history file should not exist
legacyFile := path.Join(filePath, "history")
Expect(legacyFile).NotTo(BeAnExistingFile())
// History should not exist yet
historyFile := path.Join(filePath, "history", "default.json")
Expect(historyFile).NotTo(BeAnExistingFile())
bytes, err := utils.FileToBytes("history.json")
Expect(err).NotTo(HaveOccurred())
Expect(os.WriteFile(legacyFile, bytes, 0644)).To(Succeed())
Expect(legacyFile).To(BeARegularFile())
// Perform a query
command := exec.Command(binaryPath, "--query", "some-query")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
// The CLI response should be as expected
Eventually(session).Should(gexec.Exit(exitSuccess))
output := string(session.Out.Contents())
response := `I don't have personal opinions about bars, but here are some popular bars in Red Hook, Brooklyn:`
Expect(output).To(ContainSubstring(response))
// The history file should have the expected content
Expect(path.Dir(historyFile)).To(BeADirectory())
content, err := os.ReadFile(historyFile)
Expect(err).NotTo(HaveOccurred())
Expect(content).NotTo(BeEmpty())
Expect(string(content)).To(ContainSubstring(response))
// The legacy file should now be a directory
Expect(legacyFile).To(BeADirectory())
Expect(legacyFile).NotTo(BeARegularFile())
// The content was moved to the new file
Expect(string(content)).To(ContainSubstring("Of course! Which city are you referring to?"))
})
it("should not require an API key for the --clear-history flag", func() {
Expect(os.Unsetenv(apiKeyEnvVar)).To(Succeed())
command := exec.Command(binaryPath, "--clear-history")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(exitSuccess))
})
it("keeps track of history", func() {
// History should not exist yet
historyDir := path.Join(filePath, "history")
historyFile := path.Join(historyDir, "default.json")
Expect(historyFile).NotTo(BeAnExistingFile())
// Perform a query and check response
response := `I don't have personal opinions about bars, but here are some popular bars in Red Hook, Brooklyn:`
output := runCommand("--query", "some-query")
Expect(output).To(ContainSubstring(response))
// Check if history file was created with expected content
Expect(historyDir).To(BeADirectory())
checkHistoryContent := func(expectedContent string) {
content, err := os.ReadFile(historyFile)
Expect(err).NotTo(HaveOccurred())
Expect(string(content)).To(ContainSubstring(expectedContent))
}
checkHistoryContent(response)
// Clear the history using the CLI
runCommand("--clear-history")
Expect(historyFile).NotTo(BeAnExistingFile())
// Test omitting history through environment variable
omitHistoryEnvKey := strings.Replace(apiKeyEnvVar, "API_KEY", "OMIT_HISTORY", 1)
envValue := "true"
Expect(os.Setenv(omitHistoryEnvKey, envValue)).To(Succeed())
// Perform another query with history omitted
runCommand("--query", "some-query")
// The history file should NOT be recreated
Expect(historyFile).NotTo(BeAnExistingFile())
// Cleanup: Unset the environment variable
Expect(os.Unsetenv(omitHistoryEnvKey)).To(Succeed())
})
it("should return the expected result for the --list-threads flag", func() {
historyDir := path.Join(filePath, "history")
Expect(os.Mkdir(historyDir, 0755)).To(Succeed())
files := []string{"thread1.json", "thread2.json", "thread3.json", "default.json"}
os.Mkdir(historyDir, 7555)
for _, file := range files {
file, err := os.Create(filepath.Join(historyDir, file))
Expect(err).NotTo(HaveOccurred())
Expect(file.Close()).To(Succeed())
}
output := runCommand("--list-threads")
Expect(output).To(ContainSubstring("* default (current)"))
Expect(output).To(ContainSubstring("- thread1"))
Expect(output).To(ContainSubstring("- thread2"))
Expect(output).To(ContainSubstring("- thread3"))
})
it("should delete the expected thread using the --delete-threads flag", func() {
historyDir := path.Join(filePath, "history")
Expect(os.Mkdir(historyDir, 0755)).To(Succeed())
files := []string{"thread1.json", "thread2.json", "thread3.json", "default.json"}
os.Mkdir(historyDir, 7555)
for _, file := range files {
file, err := os.Create(filepath.Join(historyDir, file))
Expect(err).NotTo(HaveOccurred())
Expect(file.Close()).To(Succeed())
}
runCommand("--delete-thread", "thread2")
output := runCommand("--list-threads")
Expect(output).To(ContainSubstring("* default (current)"))
Expect(output).To(ContainSubstring("- thread1"))
Expect(output).NotTo(ContainSubstring("- thread2"))
Expect(output).To(ContainSubstring("- thread3"))
})
it("should not throw an error when a non-existent thread is deleted using the --delete-threads flag", func() {
command := exec.Command(binaryPath, "--delete-thread", "does-not-exist")
session, err := gexec.Start(command, io.Discard, io.Discard)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gexec.Exit(exitSuccess))
})
when("configurable flags are set", func() {
it.Before(func() {
configFile = path.Join(filePath, "config.yaml")
Expect(configFile).NotTo(BeAnExistingFile())
})
it("has a configurable default model", func() {
oldModel := "gpt-3.5-turbo"
newModel := "gpt-3.5-turbo-0301"
// Verify initial model
output := runCommand("--list-models")
Expect(output).To(ContainSubstring("* " + oldModel + " (current)"))
Expect(output).To(ContainSubstring("- " + newModel))
// Update model
runCommand("--set-model", newModel)
// Check configFile is created and contains the new model
Expect(configFile).To(BeAnExistingFile())
checkConfigFileContent(newModel)
// Verify updated model through --list-models
output = runCommand("--list-models")
Expect(output).To(ContainSubstring("* " + newModel + " (current)"))
})
it("has a configurable default context-window", func() {
defaults := config.New().ReadDefaults()
// Initial check for default context-window
output := runCommand("--config")
Expect(output).To(ContainSubstring(strconv.Itoa(defaults.ContextWindow)))
// Update and verify context-window
newContextWindow := "100000"
runCommand("--set-context-window", newContextWindow)
Expect(configFile).To(BeAnExistingFile())
checkConfigFileContent(newContextWindow)
// Verify update through --config
output = runCommand("--config")
Expect(output).To(ContainSubstring(newContextWindow))
// Environment variable takes precedence
envContext := "123"
modelEnvKey := strings.Replace(apiKeyEnvVar, "API_KEY", "CONTEXT_WINDOW", 1)
Expect(os.Setenv(modelEnvKey, envContext)).To(Succeed())
// Verify environment variable override
output = runCommand("--config")
Expect(output).To(ContainSubstring(envContext))
Expect(os.Unsetenv(modelEnvKey)).To(Succeed())
})
it("has a configurable default max-tokens", func() {
defaults := config.New().ReadDefaults()
// Initial check for default max-tokens
output := runCommand("--config")
Expect(output).To(ContainSubstring(strconv.Itoa(defaults.MaxTokens)))
// Update and verify max-tokens
newMaxTokens := "81724"
runCommand("--set-max-tokens", newMaxTokens)
Expect(configFile).To(BeAnExistingFile())
checkConfigFileContent(newMaxTokens)
// Verify update through --config
output = runCommand("--config")
Expect(output).To(ContainSubstring(newMaxTokens))
// Environment variable takes precedence
modelEnvKey := strings.Replace(apiKeyEnvVar, "API_KEY", "MAX_TOKENS", 1)
Expect(os.Setenv(modelEnvKey, newMaxTokens)).To(Succeed())
// Verify environment variable override
output = runCommand("--config")
Expect(output).To(ContainSubstring(newMaxTokens))
Expect(os.Unsetenv(modelEnvKey)).To(Succeed())
})
it("has a configurable default thread", func() {
defaults := config.New().ReadDefaults()
// Initial check for default thread
output := runCommand("--config")
Expect(output).To(ContainSubstring(defaults.Thread))
// Update and verify thread
newThread := "new-thread"
runCommand("--set-thread", newThread)
Expect(configFile).To(BeAnExistingFile())
checkConfigFileContent(newThread)
// Verify update through --config
output = runCommand("--config")
Expect(output).To(ContainSubstring(newThread))
// Environment variable takes precedence
threadEnvKey := strings.Replace(apiKeyEnvVar, "API_KEY", "THREAD", 1)
Expect(os.Setenv(threadEnvKey, newThread)).To(Succeed())
// Verify environment variable override
output = runCommand("--config")
Expect(output).To(ContainSubstring(newThread))
Expect(os.Unsetenv(threadEnvKey)).To(Succeed())
})
})
})
})
}