routes_create_test.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. package server
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "net/http/httptest"
  10. "os"
  11. "path/filepath"
  12. "slices"
  13. "testing"
  14. "github.com/gin-gonic/gin"
  15. "github.com/ollama/ollama/api"
  16. )
  17. var stream bool = false
  18. func createBinFile(t *testing.T) string {
  19. t.Helper()
  20. f, err := os.CreateTemp(t.TempDir(), "")
  21. if err != nil {
  22. t.Fatal(err)
  23. }
  24. defer f.Close()
  25. if err := binary.Write(f, binary.LittleEndian, []byte("GGUF")); err != nil {
  26. t.Fatal(err)
  27. }
  28. if err := binary.Write(f, binary.LittleEndian, uint32(3)); err != nil {
  29. t.Fatal(err)
  30. }
  31. if err := binary.Write(f, binary.LittleEndian, uint64(0)); err != nil {
  32. t.Fatal(err)
  33. }
  34. if err := binary.Write(f, binary.LittleEndian, uint64(0)); err != nil {
  35. t.Fatal(err)
  36. }
  37. return f.Name()
  38. }
  39. type responseRecorder struct {
  40. *httptest.ResponseRecorder
  41. http.CloseNotifier
  42. }
  43. func NewRecorder() *responseRecorder {
  44. return &responseRecorder{
  45. ResponseRecorder: httptest.NewRecorder(),
  46. }
  47. }
  48. func (t *responseRecorder) CloseNotify() <-chan bool {
  49. return make(chan bool)
  50. }
  51. func createRequest(t *testing.T, fn func(*gin.Context), body any) *httptest.ResponseRecorder {
  52. t.Helper()
  53. w := NewRecorder()
  54. c, _ := gin.CreateTestContext(w)
  55. var b bytes.Buffer
  56. if err := json.NewEncoder(&b).Encode(body); err != nil {
  57. t.Fatal(err)
  58. }
  59. c.Request = &http.Request{
  60. Body: io.NopCloser(&b),
  61. }
  62. fn(c)
  63. return w.ResponseRecorder
  64. }
  65. func checkFileExists(t *testing.T, p string, expect []string) {
  66. t.Helper()
  67. actual, err := filepath.Glob(p)
  68. if err != nil {
  69. t.Fatal(err)
  70. }
  71. if !slices.Equal(actual, expect) {
  72. t.Fatalf("expected slices to be equal %v", actual)
  73. }
  74. }
  75. func TestCreateFromBin(t *testing.T) {
  76. p := t.TempDir()
  77. t.Setenv("OLLAMA_MODELS", p)
  78. var s Server
  79. w := createRequest(t, s.CreateModelHandler, api.CreateRequest{
  80. Name: "test",
  81. Modelfile: fmt.Sprintf("FROM %s", createBinFile(t)),
  82. Stream: &stream,
  83. })
  84. if w.Code != http.StatusOK {
  85. t.Fatalf("expected status code 200, actual %d", w.Code)
  86. }
  87. checkFileExists(t, filepath.Join(p, "manifests", "*", "*", "*", "*"), []string{
  88. filepath.Join(p, "manifests", "registry.ollama.ai", "library", "test", "latest"),
  89. })
  90. checkFileExists(t, filepath.Join(p, "blobs", "*"), []string{
  91. filepath.Join(p, "blobs", "sha256-a4e5e156ddec27e286f75328784d7106b60a4eb1d246e950a001a3f944fbda99"),
  92. filepath.Join(p, "blobs", "sha256-ca239d7bd8ea90e4a5d2e6bf88f8d74a47b14336e73eb4e18bed4dd325018116"),
  93. })
  94. }
  95. func TestCreateFromModel(t *testing.T) {
  96. p := t.TempDir()
  97. t.Setenv("OLLAMA_MODELS", p)
  98. var s Server
  99. w := createRequest(t, s.CreateModelHandler, api.CreateRequest{
  100. Name: "test",
  101. Modelfile: fmt.Sprintf("FROM %s", createBinFile(t)),
  102. Stream: &stream,
  103. })
  104. if w.Code != http.StatusOK {
  105. t.Fatalf("expected status code 200, actual %d", w.Code)
  106. }
  107. checkFileExists(t, filepath.Join(p, "manifests", "*", "*", "*", "*"), []string{
  108. filepath.Join(p, "manifests", "registry.ollama.ai", "library", "test", "latest"),
  109. })
  110. w = createRequest(t, s.CreateModelHandler, api.CreateRequest{
  111. Name: "test2",
  112. Modelfile: "FROM test",
  113. Stream: &stream,
  114. })
  115. if w.Code != http.StatusOK {
  116. t.Fatalf("expected status code 200, actual %d", w.Code)
  117. }
  118. checkFileExists(t, filepath.Join(p, "manifests", "*", "*", "*", "*"), []string{
  119. filepath.Join(p, "manifests", "registry.ollama.ai", "library", "test", "latest"),
  120. filepath.Join(p, "manifests", "registry.ollama.ai", "library", "test2", "latest"),
  121. })
  122. checkFileExists(t, filepath.Join(p, "blobs", "*"), []string{
  123. filepath.Join(p, "blobs", "sha256-a4e5e156ddec27e286f75328784d7106b60a4eb1d246e950a001a3f944fbda99"),
  124. filepath.Join(p, "blobs", "sha256-ca239d7bd8ea90e4a5d2e6bf88f8d74a47b14336e73eb4e18bed4dd325018116"),
  125. })
  126. }