Add full shell, replaceable Omics adapter and bilingual interaction
Implement approved specification #32 and tickets #33-#37. Keep host authentication server-verified and pin session interaction language. Compile scoped base selectors for browser compatibility and retain full gutters during CSS pruning.
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/aritmolab/thothii/tools/tht/internal/config"
|
||||
)
|
||||
|
||||
func TestInstallationGeneratePublishesShellWithoutDockerOrModelChanges(t *testing.T) {
|
||||
fixture := newCLIFixture(t, "")
|
||||
fixture.setEnvironment(t)
|
||||
fixture.generateModelProjection(t)
|
||||
descriptor, err := os.ReadFile(fixture.installationPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
descriptor = append(descriptor, []byte("shell:\n mode: full\n defaultLocale: fr-CA\n")...)
|
||||
if err := os.WriteFile(fixture.installationPath, descriptor, 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
installation, err := config.Load(fixture.installationPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
preserved := make(map[string][]byte)
|
||||
for _, path := range []string{fixture.installationPath, fixture.envFile, installation.GeneratedModelCatalogPath(), installation.GeneratedPiModelsPath(), installation.GeneratedPiSettingsPath()} {
|
||||
preserved[path], err = os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
var stdout, stderr bytes.Buffer
|
||||
args := []string{"--installation", fixture.installationPath, "installation", "generate"}
|
||||
if code := run(context.Background(), args, &stdout, &stderr); code != 0 {
|
||||
t.Fatalf("installation generate exit = %d: %s", code, stderr.String())
|
||||
}
|
||||
public, err := os.ReadFile(installation.GeneratedFrontendConfigPath())
|
||||
if err != nil || !bytes.Contains(public, []byte(`"mode": "full"`)) || !bytes.Contains(public, []byte(`"defaultLocale": "fr-CA"`)) || bytes.Contains(public, []byte(`"adapter"`)) {
|
||||
t.Fatalf("generated public shell = %s, %v", public, err)
|
||||
}
|
||||
for path, before := range preserved {
|
||||
after, err := os.ReadFile(path)
|
||||
if err != nil || !bytes.Equal(before, after) {
|
||||
t.Fatalf("installation generate changed authored configuration or model projection %s: %v", path, err)
|
||||
}
|
||||
}
|
||||
before, err := os.Stat(installation.GeneratedDirectory())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if code := run(context.Background(), args, &stdout, &stderr); code != 0 {
|
||||
t.Fatalf("repeated installation generate exit = %d: %s", code, stderr.String())
|
||||
}
|
||||
after, err := os.Stat(installation.GeneratedDirectory())
|
||||
if err != nil || !os.SameFile(before, after) {
|
||||
t.Fatalf("identical installation generate replaced the generation: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(fixture.argsFile); !os.IsNotExist(err) {
|
||||
t.Fatalf("installation generate invoked Docker: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstallationGenerateRejectsArgumentsBeforePublication(t *testing.T) {
|
||||
fixture := newCLIFixture(t, "")
|
||||
fixture.setEnvironment(t)
|
||||
var stdout, stderr bytes.Buffer
|
||||
if code := run(context.Background(), []string{"--installation", fixture.installationPath, "installation", "generate", "--build"}, &stdout, &stderr); code != 2 {
|
||||
t.Fatalf("exit = %d, want 2: %s", code, stderr.String())
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(fixture.root, "generated")); !os.IsNotExist(err) {
|
||||
t.Fatalf("invalid arguments published projections: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -41,10 +41,13 @@ descriptor in the current project tree.
|
||||
|
||||
Commands:
|
||||
setup [--configure-only] [--installation-id ID] [--profile local|server]
|
||||
[--shell-mode full|embedded] [--shell-default-locale BCP47-TAG] [--shell-adapter omics-portal]
|
||||
Create or validate the local non-secret installation configuration.
|
||||
installation migrate --output PATH --session-default PROVIDER/MODEL
|
||||
--embedding-id PROVIDER/MODEL --embedding-dimensions N
|
||||
Create a review-only schema-v2 candidate from all three legacy model sources.
|
||||
installation generate
|
||||
Publish runtime projections from the descriptor without invoking Docker.
|
||||
version [--json] Show the host CLI build identity.
|
||||
auth configure --mode local|oidc ...
|
||||
Configure local users or OIDC group mapping; see tht auth for exact options.
|
||||
@@ -127,6 +130,9 @@ func run(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
||||
return versionCommand(commandArgs, stdout, stderr)
|
||||
}
|
||||
if command == "installation" {
|
||||
if len(commandArgs) > 0 && commandArgs[0] == "generate" {
|
||||
return installationGenerationCommand(installationPath, commandArgs[1:], stdout, stderr)
|
||||
}
|
||||
return installationMigrationCommand(installationPath, commandArgs, stdout, stderr)
|
||||
}
|
||||
return commandUsageError(stderr, fmt.Sprintf("unknown command %q", command))
|
||||
@@ -258,6 +264,32 @@ func isBootstrapCommand(command string) bool {
|
||||
return command == "help" || command == "setup" || command == "version" || command == "installation"
|
||||
}
|
||||
|
||||
func installationGenerationCommand(installationPath string, args []string, stdout, stderr io.Writer) int {
|
||||
if len(args) != 0 {
|
||||
return commandUsageError(stderr, "installation generate does not accept arguments")
|
||||
}
|
||||
workingDirectory, err := os.Getwd()
|
||||
if err != nil {
|
||||
return commandUsageError(stderr, "current directory is unavailable")
|
||||
}
|
||||
installationPath, err = config.Resolve(installationPath, os.Getenv, workingDirectory)
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "tht: %s\n", output.Sanitize(err.Error(), nil))
|
||||
return 2
|
||||
}
|
||||
installation, err := config.Load(installationPath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "tht: %s\n", output.Sanitize(err.Error(), nil))
|
||||
return 2
|
||||
}
|
||||
if err := modelprojection.Generate(installation); err != nil {
|
||||
fmt.Fprintf(stderr, "tht: %s\n", output.Sanitize(err.Error(), nil))
|
||||
return 2
|
||||
}
|
||||
fmt.Fprintf(stdout, "Runtime projections generated in %s.\n", installation.GeneratedDirectory())
|
||||
return 0
|
||||
}
|
||||
|
||||
func installationMigrationCommand(installationPath string, args []string, stdout, stderr io.Writer) int {
|
||||
if installationPath == "" {
|
||||
return commandUsageError(stderr, "installation migrate requires --installation with the legacy descriptor")
|
||||
@@ -395,6 +427,12 @@ func parseSetupArgs(args []string) (setup.Request, error) {
|
||||
target = &request.InstallationID
|
||||
case "--profile":
|
||||
target = &request.Profile
|
||||
case "--shell-mode":
|
||||
target = &request.Answers.ShellMode
|
||||
case "--shell-default-locale":
|
||||
target = &request.Answers.ShellDefaultLocale
|
||||
case "--shell-adapter":
|
||||
target = &request.Answers.ShellAdapter
|
||||
case "--workspace-remote":
|
||||
target = &request.Answers.WorkspaceRemote
|
||||
case "--workspace-branch":
|
||||
|
||||
@@ -362,6 +362,7 @@ func TestParseSetupArgsAcceptsExplicitNonInteractiveAnswers(t *testing.T) {
|
||||
"--workspace-remote", "https://git.example.invalid/workspaces.git", "--workspace-branch", "release",
|
||||
"--workspace-access", "https", "--secrets-file", "/tmp/thothii.secrets", "--pi-auth-file", "/tmp/pi-auth.json",
|
||||
"--git-credentials-file", "/tmp/git-credentials", "--git-ca-file", "/tmp/git-ca.pem",
|
||||
"--shell-mode", "full", "--shell-default-locale", "it", "--shell-adapter", "omics-portal",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -372,6 +373,9 @@ func TestParseSetupArgsAcceptsExplicitNonInteractiveAnswers(t *testing.T) {
|
||||
if request.Answers.WorkspaceBranch != "release" || request.Answers.GitCAFile != "/tmp/git-ca.pem" {
|
||||
t.Fatalf("setup answers = %#v", request.Answers)
|
||||
}
|
||||
if request.Answers.ShellMode != "full" || request.Answers.ShellDefaultLocale != "it" || request.Answers.ShellAdapter != "omics-portal" {
|
||||
t.Fatalf("setup shell answers = %#v", request.Answers)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPiStatusUsesInstallationEnvironmentWithoutFlag(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user