mirror of https://github.com/ollama/ollama
327 lines
16 KiB
Go
327 lines
16 KiB
Go
package updater
|
|
|
|
import (
|
|
"archive/zip"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestDoUpgrade(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
BundlePath = filepath.Join(tmpDir, "Ollama.app")
|
|
appContents := filepath.Join(BundlePath, "Contents")
|
|
appBackupDir = filepath.Join(tmpDir, "backup")
|
|
appContentsOld := filepath.Join(appBackupDir, "Ollama.app", "Contents")
|
|
UpdateStageDir = filepath.Join(tmpDir, "updates")
|
|
UpgradeMarkerFile = filepath.Join(tmpDir, "upgraded")
|
|
bundle := filepath.Join(UpdateStageDir, "foo", "ollama-darwin.zip")
|
|
|
|
err := os.MkdirAll(filepath.Join(appContents, "MacOS"), 0o755)
|
|
if err != nil {
|
|
t.Fatal("failed to create empty dirs")
|
|
}
|
|
err = os.MkdirAll(filepath.Join(BundlePath, "Contents", "Resources"), 0o755)
|
|
if err != nil {
|
|
t.Fatal("failed to create empty dirs")
|
|
}
|
|
err = os.MkdirAll(filepath.Dir(bundle), 0o755)
|
|
if err != nil {
|
|
t.Fatal("failed to create empty dirs")
|
|
}
|
|
|
|
// No update file, simple failure scenario
|
|
if err := DoUpgrade(false); err == nil {
|
|
t.Fatal("expected failure without download")
|
|
} else if !strings.Contains(err.Error(), "failed to lookup downloads") {
|
|
t.Fatalf("unexpected error: %s", err.Error())
|
|
}
|
|
|
|
// Start with an unreadable zip file
|
|
if err := os.WriteFile(bundle, []byte{0x4b, 0x50, 0x40, 0x03, 0x00, 0x0a, 0x00}, 0o755); err != nil {
|
|
t.Fatalf("failed to create intentionally corrupt zip file: %s", err)
|
|
}
|
|
if err := DoUpgrade(false); err == nil {
|
|
t.Fatal("expected failure with corrupt zip file")
|
|
} else if !strings.Contains(err.Error(), "unable to open upgrade bundle") {
|
|
t.Fatalf("unexpected error with corrupt zip file: %s", err)
|
|
}
|
|
|
|
// Generate valid (partial) zip file for remaining scenarios
|
|
if err := zipCreationHelper(bundle, []testPayload{
|
|
{
|
|
Name: "Ollama.app/Contents/MacOS/Ollama",
|
|
Body: []byte("would be app binary"),
|
|
},
|
|
{
|
|
Name: "Ollama.app/Contents/Resources/ollama",
|
|
Body: []byte("would be the cli"),
|
|
},
|
|
{
|
|
Name: "Ollama.app/Contents/Resources/dummy",
|
|
Body: []byte("./ollama"),
|
|
Mode: os.ModeSymlink,
|
|
},
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Permission failure on rename
|
|
if err := os.Chmod(BundlePath, 0o500); err != nil {
|
|
t.Fatal("failed to remove write permission")
|
|
}
|
|
if err := DoUpgrade(false); err == nil {
|
|
t.Fatal("expected failure with no permission to rename Contents")
|
|
} else if !strings.Contains(err.Error(), "permission problems") {
|
|
t.Fatalf("unexpected error with permission failure: %s", err)
|
|
}
|
|
if err := os.Chmod(BundlePath, 0o755); err != nil {
|
|
t.Fatal("failed to restore write permission")
|
|
}
|
|
|
|
// Prior failed upgrade
|
|
if err := os.MkdirAll(appContentsOld, 0o755); err != nil {
|
|
t.Fatal("failed to create empty dirs")
|
|
}
|
|
if err := DoUpgrade(false); err == nil {
|
|
t.Fatal("expected failure with old contents existing")
|
|
} else if !strings.Contains(err.Error(), "prior upgrade failed") {
|
|
t.Fatalf("unexpected error with old contents: %s", err)
|
|
}
|
|
if err := os.RemoveAll(appBackupDir); err != nil {
|
|
t.Fatal("failed to cleanup dir")
|
|
}
|
|
|
|
// TODO - a failure mode where we revert the backup
|
|
|
|
// Happy path
|
|
if err := DoUpgrade(false); err != nil {
|
|
t.Fatalf("unexpected error with clean setup: %s", err)
|
|
}
|
|
if _, err := os.Stat(appContentsOld); err != nil {
|
|
t.Fatalf("missing %s", appContentsOld)
|
|
}
|
|
if _, err := os.Stat(UpgradeMarkerFile); err != nil {
|
|
t.Fatalf("missing marker %s", UpgradeMarkerFile)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(BundlePath, "Contents", "MacOS", "Ollama")); err != nil {
|
|
t.Fatalf("missing new App")
|
|
}
|
|
if _, err := os.Stat(filepath.Join(BundlePath, "Contents", "Resources", "ollama")); err != nil {
|
|
t.Fatalf("missing new cli")
|
|
}
|
|
|
|
// Cleanup before next attempt
|
|
if err := DoPostUpgradeCleanup(); err != nil {
|
|
t.Fatal("failed to cleanup dir")
|
|
}
|
|
|
|
err = os.MkdirAll(filepath.Dir(bundle), 0o755)
|
|
if err != nil {
|
|
t.Fatal("failed to create empty dirs")
|
|
}
|
|
|
|
// Zip file with one corrupt file within to trigger a rollback
|
|
if err := os.WriteFile(bundle, corruptZipData, 0o755); err != nil {
|
|
t.Fatalf("failed to create intentionally corrupt zip file: %s", err)
|
|
}
|
|
if err := DoUpgrade(false); err == nil {
|
|
t.Fatal("expected failure with corrupt zip file")
|
|
} else if !strings.Contains(err.Error(), "failed to open bundle file") {
|
|
t.Fatalf("unexpected error with corrupt zip file: %s", err)
|
|
}
|
|
// Make sure things were restored on partial failure
|
|
if _, err := os.Stat(appContents); err != nil {
|
|
t.Fatalf("missing %s", appContents)
|
|
}
|
|
if _, err := os.Stat(appContentsOld); err == nil {
|
|
t.Fatal("old contents still exists")
|
|
}
|
|
if _, err := os.Stat(filepath.Join(BundlePath, "Contents", "MacOS", "Ollama")); err != nil {
|
|
t.Fatalf("missing old App")
|
|
}
|
|
if _, err := os.Stat(filepath.Join(BundlePath, "Contents", "Resources", "ollama")); err != nil {
|
|
t.Fatalf("missing old cli")
|
|
}
|
|
}
|
|
|
|
func TestDoUpgradeAtStartup(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
BundlePath = filepath.Join(tmpDir, "Ollama.app")
|
|
appBackupDir = filepath.Join(tmpDir, "backup")
|
|
UpdateStageDir = filepath.Join(tmpDir, "updates")
|
|
UpgradeMarkerFile = filepath.Join(tmpDir, "upgraded")
|
|
bundle := filepath.Join(UpdateStageDir, "foo", "ollama-darwin.zip")
|
|
|
|
if err := DoUpgradeAtStartup(); err == nil {
|
|
t.Fatal("expected failure without download")
|
|
} else if !strings.Contains(err.Error(), "failed to lookup downloads") {
|
|
t.Fatalf("unexpected error: %s", err.Error())
|
|
}
|
|
|
|
if err := os.MkdirAll(filepath.Dir(bundle), 0o755); err != nil {
|
|
t.Fatal("failed to create empty dirs")
|
|
}
|
|
|
|
if err := zipCreationHelper(bundle, []testPayload{
|
|
{
|
|
Name: "Ollama.app/Contents/MacOS/Ollama",
|
|
Body: []byte("would be app binary"),
|
|
},
|
|
{
|
|
Name: "Ollama.app/Contents/Resources/ollama",
|
|
Body: []byte("would be the cli"),
|
|
},
|
|
{
|
|
Name: "Ollama.app/Contents/Resources/dummy",
|
|
Body: []byte("./ollama"),
|
|
Mode: os.ModeSymlink,
|
|
},
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := DoUpgradeAtStartup(); err != nil {
|
|
t.Fatalf("unexpected error with verification failure: %s", err)
|
|
}
|
|
if _, err := os.Stat(bundle); err == nil {
|
|
t.Fatalf("unverified bundle still exists %s", bundle)
|
|
}
|
|
}
|
|
|
|
func TestVerifyDownloadFailures(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
BundlePath = filepath.Join(tmpDir, "Ollama.app")
|
|
UpdateStageDir = filepath.Join(tmpDir, "staging")
|
|
bundle := filepath.Join(UpdateStageDir, "foo", "ollama-darwin.zip")
|
|
if err := os.MkdirAll(filepath.Dir(bundle), 0o755); err != nil {
|
|
t.Fatal("failed to create empty dirs")
|
|
}
|
|
tests := []struct {
|
|
n string
|
|
in []testPayload
|
|
expected string
|
|
}{
|
|
{"breakout", []testPayload{
|
|
{
|
|
Name: "Ollama.app/",
|
|
Body: []byte{},
|
|
}, {
|
|
Name: "Ollama.app/Resources/ollama",
|
|
Body: []byte("cli payload here"),
|
|
}, {
|
|
Name: "Ollama.app/Contents/MacOS/Ollama",
|
|
Body: []byte("../../../../breakout"),
|
|
Mode: os.ModeSymlink,
|
|
},
|
|
}, "bundle contains link outside"},
|
|
{"absolute", []testPayload{{
|
|
Name: "Ollama.app/Contents/MacOS/Ollama",
|
|
Body: []byte("/etc/foo"),
|
|
Mode: os.ModeSymlink,
|
|
}}, "bundle contains absolute"},
|
|
{"missing", []testPayload{{
|
|
Name: "Ollama.app/Contents/MacOS/Ollama",
|
|
Body: []byte("../nothere"),
|
|
Mode: os.ModeSymlink,
|
|
}}, "no such file or directory"},
|
|
{"unsigned", []testPayload{{
|
|
Name: "Ollama.app/Contents/MacOS/Ollama",
|
|
Body: []byte{0xfa, 0xcf, 0xfe, 0xed, 0x00, 0x0c, 0x01, 0x00},
|
|
}}, "signature verification failed"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.n, func(t *testing.T) {
|
|
_ = os.Remove(bundle)
|
|
if err := zipCreationHelper(bundle, tt.in); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err := VerifyDownload()
|
|
if err == nil || !strings.Contains(err.Error(), tt.expected) {
|
|
t.Fatalf("expected \"%s\" got %s", tt.expected, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// One file has been corrupted to cause a checksum mismatch
|
|
var corruptZipData = []byte{0x50, 0x4b, 0x3, 0x4, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0xed, 0x7e, 0x8f, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x0, 0x1c, 0x0, 0x4f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x2e, 0x61, 0x70, 0x70, 0x2f, 0x55, 0x54, 0x9, 0x0, 0x3, 0x6d, 0x6c, 0x5f, 0x67, 0x6e, 0x6c, 0x5f, 0x67, 0x75, 0x78, 0xb, 0x0, 0x1, 0x4, 0xf5, 0x1, 0x0, 0x0, 0x4, 0x14, 0x0, 0x0, 0x0, 0x50, 0x4b, 0x3, 0x4, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd8, 0x7e, 0x8f, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14, 0x0, 0x1c, 0x0, 0x4f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x2e, 0x61, 0x70, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x55, 0x54, 0x9, 0x0, 0x3, 0x48, 0x6c, 0x5f, 0x67, 0x58, 0x6c, 0x5f, 0x67, 0x75, 0x78, 0xb, 0x0, 0x1, 0x4, 0xf5, 0x1, 0x0, 0x0, 0x4, 0x14, 0x0, 0x0, 0x0, 0x50, 0x4b, 0x3, 0x4, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe3, 0x7e, 0x8f, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x1c, 0x0, 0x4f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x2e, 0x61, 0x70, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x4d, 0x61, 0x63, 0x4f, 0x53, 0x2f, 0x55, 0x54, 0x9, 0x0, 0x3, 0x59, 0x6c, 0x5f, 0x67, 0x9f, 0x6c, 0x5f, 0x67, 0x75, 0x78, 0xb, 0x0, 0x1, 0x4, 0xf5, 0x1, 0x0, 0x0, 0x4, 0x14, 0x0, 0x0, 0x0, 0x50, 0x4b, 0x3, 0x4, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe3, 0x7e, 0x8f, 0x59, 0xe3, 0x6, 0x15, 0x70, 0x14, 0x0, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x20, 0x0, 0x1c, 0x0, 0x4f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x2e, 0x61, 0x70, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x4d, 0x61, 0x63, 0x4f, 0x53, 0x2f, 0x4f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x55, 0x54, 0x9, 0x0, 0x3, 0x59, 0x6c, 0x5f, 0x67, 0x83, 0x6c, 0x5f, 0x67, 0x75, 0x78, 0xb, 0x0, 0x1, 0x4, 0xf5, 0x1, 0x0, 0x0, 0x4, 0x14, 0x0, 0x0, 0x0, 0x43, 0x4f, 0x52, 0x52, 0x55, 0x50, 0x54, 0xa, 0x50, 0x4b, 0x3, 0x4, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe9, 0x7e, 0x8f, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x1c, 0x0, 0x4f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x2e, 0x61, 0x70, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x55, 0x54, 0x9, 0x0, 0x3, 0x66, 0x6c, 0x5f, 0x67, 0x83, 0x6c, 0x5f, 0x67, 0x75, 0x78, 0xb, 0x0, 0x1, 0x4, 0xf5, 0x1, 0x0, 0x0, 0x4, 0x14, 0x0, 0x0, 0x0, 0x50, 0x4b, 0x3, 0x4, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe9, 0x7e, 0x8f, 0x59, 0x19, 0xa5, 0x62, 0xf7, 0x11, 0x0, 0x0, 0x0, 0x11, 0x0, 0x0, 0x0, 0x24, 0x0, 0x1c, 0x0, 0x4f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x2e, 0x61, 0x70, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x6f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x55, 0x54, 0x9, 0x0, 0x3, 0x66, 0x6c, 0x5f, 0x67, 0x66, 0x6c, 0x5f, 0x67, 0x75, 0x78, 0xb, 0x0, 0x1, 0x4, 0xf5, 0x1, 0x0, 0x0, 0x4, 0x14, 0x0, 0x0, 0x0, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0xa, 0x50, 0x4b, 0x1, 0x2, 0x1e, 0x3, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0xed, 0x7e, 0x8f, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0xed, 0x41, 0x0, 0x0, 0x0, 0x0, 0x4f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x2e, 0x61, 0x70, 0x70, 0x2f, 0x55, 0x54, 0x5, 0x0, 0x3, 0x6d, 0x6c, 0x5f, 0x67, 0x75, 0x78, 0xb, 0x0, 0x1, 0x4, 0xf5, 0x1, 0x0, 0x0, 0x4, 0x14, 0x0, 0x0, 0x0, 0x50, 0x4b, 0x1, 0x2, 0x1e, 0x3, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd8, 0x7e, 0x8f, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0xed, 0x41, 0x45, 0x0, 0x0, 0x0, 0x4f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x2e, 0x61, 0x70, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x55, 0x54, 0x5, 0x0, 0x3, 0x48, 0x6c, 0x5f, 0x67, 0x75, 0x78, 0xb, 0x0, 0x1, 0x4, 0xf5, 0x1, 0x0, 0x0, 0x4, 0x14, 0x0, 0x0, 0x0, 0x50, 0x4b, 0x1, 0x2, 0x1e, 0x3, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe3, 0x7e, 0x8f, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0xed, 0x41, 0x93, 0x0, 0x0, 0x0, 0x4f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x2e, 0x61, 0x70, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x4d, 0x61, 0x63, 0x4f, 0x53, 0x2f, 0x55, 0x54, 0x5, 0x0, 0x3, 0x59, 0x6c, 0x5f, 0x67, 0x75, 0x78, 0xb, 0x0, 0x1, 0x4, 0xf5, 0x1, 0x0, 0x0, 0x4, 0x14, 0x0, 0x0, 0x0, 0x50, 0x4b, 0x1, 0x2, 0x1e, 0x3, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe3, 0x7e, 0x8f, 0x59, 0xe3, 0x6, 0x15, 0x70, 0x14, 0x0, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x20, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa4, 0x81, 0xe7, 0x0, 0x0, 0x0, 0x4f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x2e, 0x61, 0x70, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x4d, 0x61, 0x63, 0x4f, 0x53, 0x2f, 0x4f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x55, 0x54, 0x5, 0x0, 0x3, 0x59, 0x6c, 0x5f, 0x67, 0x75, 0x78, 0xb, 0x0, 0x1, 0x4, 0xf5, 0x1, 0x0, 0x0, 0x4, 0x14, 0x0, 0x0, 0x0, 0x50, 0x4b, 0x1, 0x2, 0x1e, 0x3, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe9, 0x7e, 0x8f, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0xed, 0x41, 0x55, 0x1, 0x0, 0x0, 0x4f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x2e, 0x61, 0x70, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x55, 0x54, 0x5, 0x0, 0x3, 0x66, 0x6c, 0x5f, 0x67, 0x75, 0x78, 0xb, 0x0, 0x1, 0x4, 0xf5, 0x1, 0x0, 0x0, 0x4, 0x14, 0x0, 0x0, 0x0, 0x50, 0x4b, 0x1, 0x2, 0x1e, 0x3, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe9, 0x7e, 0x8f, 0x59, 0x19, 0xa5, 0x62, 0xf7, 0x11, 0x0, 0x0, 0x0, 0x11, 0x0, 0x0, 0x0, 0x24, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa4, 0x81, 0xad, 0x1, 0x0, 0x0, 0x4f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x2e, 0x61, 0x70, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x6f, 0x6c, 0x6c, 0x61, 0x6d, 0x61, 0x55, 0x54, 0x5, 0x0, 0x3, 0x66, 0x6c, 0x5f, 0x67, 0x75, 0x78, 0xb, 0x0, 0x1, 0x4, 0xf5, 0x1, 0x0, 0x0, 0x4, 0x14, 0x0, 0x0, 0x0, 0x50, 0x4b, 0x5, 0x6, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x6, 0x0, 0x3f, 0x2, 0x0, 0x0, 0x1c, 0x2, 0x0, 0x0, 0x0, 0x0}
|
|
|
|
type testPayload struct {
|
|
Name string
|
|
Body []byte
|
|
Mode fs.FileMode
|
|
}
|
|
|
|
func zipCreationHelper(filename string, files []testPayload) error {
|
|
fd, err := os.Create(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
w := zip.NewWriter(fd)
|
|
for _, file := range files {
|
|
fh := &zip.FileHeader{
|
|
Name: file.Name,
|
|
Flags: 0,
|
|
}
|
|
if file.Mode != 0 {
|
|
fh.SetMode(file.Mode)
|
|
}
|
|
f, err := w.CreateHeader(fh)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = f.Write(file.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return w.Close()
|
|
}
|
|
|
|
func TestAlreadyMoved(t *testing.T) {
|
|
oldPath := SystemWidePath
|
|
defer func() {
|
|
SystemWidePath = oldPath
|
|
}()
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
t.Fatal("failed to find executable path")
|
|
}
|
|
tmpDir := t.TempDir()
|
|
testApp := filepath.Join(tmpDir, "Ollama.app")
|
|
err = os.MkdirAll(filepath.Join(testApp, "Contents", "MacOS"), 0o755)
|
|
if err != nil {
|
|
t.Fatal("failed to create Contents dir")
|
|
}
|
|
SystemWidePath = testApp
|
|
testBinary := filepath.Join(testApp, "Contents", "MacOS", "Ollama")
|
|
if err := os.Symlink(exe, testBinary); err != nil {
|
|
t.Fatalf("failed to create symlink to executable: %s", err)
|
|
}
|
|
|
|
bundle := alreadyMoved()
|
|
if bundle != testApp {
|
|
t.Fatalf("expected %s, got %s", testApp, bundle)
|
|
}
|
|
|
|
// "Keep scenario"
|
|
testApp = filepath.Join(tmpDir, "Ollama 2.app")
|
|
err = os.MkdirAll(filepath.Join(testApp, "Contents", "MacOS"), 0o755)
|
|
if err != nil {
|
|
t.Fatal("failed to create Contents dir")
|
|
}
|
|
testBinary = filepath.Join(testApp, "Contents", "MacOS", "Ollama")
|
|
if err := os.Symlink(exe, testBinary); err != nil {
|
|
t.Fatalf("failed to create symlink to executable: %s", err)
|
|
}
|
|
|
|
bundle = alreadyMoved()
|
|
if bundle != testApp {
|
|
t.Fatalf("expected %s, got %s", testApp, bundle)
|
|
}
|
|
}
|