2023-06-05 17:30:55 -07:00

544 lines
12 KiB
Go

package state
import (
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/hashicorp/vagrant-plugin-sdk/proto/vagrant_plugin_sdk"
"gorm.io/gorm"
)
func TestProject_Create(t *testing.T) {
t.Run("Requires name, path, and basis", func(t *testing.T) {
require, db := RequireAndDB(t)
result := db.Save(&Project{})
require.Error(result.Error)
require.ErrorContains(result.Error, "Name:")
require.ErrorContains(result.Error, "Path:")
require.ErrorContains(result.Error, "Basis:")
})
t.Run("Requires name", func(t *testing.T) {
require, db := RequireAndDB(t)
result := db.Save(
&Project{
Path: "/dev/null",
Basis: TestBasis(t, db),
},
)
require.Error(result.Error)
require.ErrorContains(result.Error, "Name:")
})
t.Run("Requires path", func(t *testing.T) {
require, db := RequireAndDB(t)
result := db.Save(
&Project{
Name: "default",
Basis: TestBasis(t, db),
},
)
require.Error(result.Error)
require.ErrorContains(result.Error, "Path:")
})
t.Run("Requires basis", func(t *testing.T) {
require, db := RequireAndDB(t)
result := db.Save(
&Project{
Name: "default",
Path: "/dev/null",
},
)
require.Error(result.Error)
require.ErrorContains(result.Error, "Basis:")
})
t.Run("Sets resource ID", func(t *testing.T) {
require, db := RequireAndDB(t)
project := Project{
Name: "default",
Path: "/dev/null",
Basis: TestBasis(t, db),
}
result := db.Save(&project)
require.NoError(result.Error)
require.NotEmpty(project.ResourceId)
})
t.Run("Retains resource ID", func(t *testing.T) {
require, db := RequireAndDB(t)
rid := "RESOURCE_ID"
project := Project{
Name: "default",
Path: "/dev/null",
ResourceId: rid,
Basis: TestBasis(t, db),
}
result := db.Save(&project)
require.NoError(result.Error)
require.EqualValues(rid, project.ResourceId)
})
t.Run("Does not allow duplicate name in same basis", func(t *testing.T) {
require, db := RequireAndDB(t)
basis := TestBasis(t, db)
result := db.Save(
&Project{
Name: "default",
Path: "/dev/null",
Basis: basis,
},
)
require.NoError(result.Error)
result = db.Save(
&Project{
Name: "default",
Path: "/dev/null/other",
Basis: basis,
},
)
require.Error(result.Error)
require.ErrorContains(result.Error, "Name:")
})
t.Run("Allows duplicate name in different basis", func(t *testing.T) {
require, db := RequireAndDB(t)
result := db.Save(
&Project{
Name: "default",
Path: "/dev/null",
Basis: TestBasis(t, db),
},
)
require.NoError(result.Error)
result = db.Save(
&Project{
Name: "default",
Path: "/dev/null/other",
Basis: TestBasis(t, db),
},
)
require.NoError(result.Error)
})
t.Run("Does not allow duplicate path in same basis", func(t *testing.T) {
require, db := RequireAndDB(t)
basis := TestBasis(t, db)
result := db.Save(
&Project{
Name: "default",
Path: "/dev/null",
Basis: basis,
},
)
require.NoError(result.Error)
result = db.Save(
&Project{
Name: "other",
Path: "/dev/null",
Basis: basis,
},
)
require.Error(result.Error)
require.ErrorContains(result.Error, "Path:")
})
t.Run("Allows duplicate path in different basis", func(t *testing.T) {
require, db := RequireAndDB(t)
result := db.Save(
&Project{
Name: "default",
Path: "/dev/null",
Basis: TestBasis(t, db),
},
)
require.NoError(result.Error)
result = db.Save(
&Project{
Name: "other",
Path: "/dev/null",
Basis: TestBasis(t, db),
},
)
require.NoError(result.Error)
})
t.Run("Does not allow duplicate resource IDs", func(t *testing.T) {
require, db := RequireAndDB(t)
rid := "RESOURCE ID"
result := db.Save(
&Project{
Name: "default",
Path: "/dev/null",
ResourceId: rid,
Basis: TestBasis(t, db),
},
)
require.NoError(result.Error)
result = db.Save(
&Project{
Name: "other",
Path: "/dev/null/other",
ResourceId: rid,
Basis: TestBasis(t, db),
},
)
require.Error(result.Error)
require.ErrorContains(result.Error, "ResourceId:")
})
t.Run("Creates Vagrantfile when set", func(t *testing.T) {
require, db := RequireAndDB(t)
vagrantfile := Vagrantfile{}
project := Project{
Name: "default",
Path: "/dev/null",
Basis: TestBasis(t, db),
Vagrantfile: &vagrantfile,
}
result := db.Save(&project)
require.NoError(result.Error)
require.NotNil(project.VagrantfileID)
require.Equal(*project.VagrantfileID, vagrantfile.ID)
})
}
func TestProject_Update(t *testing.T) {
t.Run("Requires name and path", func(t *testing.T) {
require, db := RequireAndDB(t)
project := &Project{
Name: "default",
Path: "/dev/null",
Basis: TestBasis(t, db),
}
result := db.Save(project)
require.NoError(result.Error)
project.Name = ""
project.Path = ""
result = db.Save(project)
require.Error(result.Error)
require.ErrorContains(result.Error, "Name:")
require.ErrorContains(result.Error, "Path:")
})
t.Run("Requires name", func(t *testing.T) {
require, db := RequireAndDB(t)
project := &Project{
Name: "default",
Path: "/dev/null",
Basis: TestBasis(t, db),
}
result := db.Save(project)
require.NoError(result.Error)
project.Name = ""
result = db.Save(project)
require.Error(result.Error)
require.ErrorContains(result.Error, "Name:")
})
t.Run("Requires path", func(t *testing.T) {
require, db := RequireAndDB(t)
project := &Project{
Name: "default",
Path: "/dev/null",
Basis: TestBasis(t, db),
}
result := db.Save(project)
require.NoError(result.Error)
project.Path = ""
result = db.Save(project)
require.Error(result.Error)
require.ErrorContains(result.Error, "Path:")
})
t.Run("Requires basis", func(t *testing.T) {
require, db := RequireAndDB(t)
project := &Project{
Name: "default",
Path: "/dev/null",
Basis: TestBasis(t, db),
}
result := db.Save(project)
require.NoError(result.Error)
project.Basis = nil
project.BasisID = 0
result = db.Save(project)
require.Error(result.Error)
})
t.Run("Does not update resource ID", func(t *testing.T) {
require, db := RequireAndDB(t)
project := Project{
Name: "default",
Path: "/dev/null",
Basis: TestBasis(t, db),
}
result := db.Save(&project)
require.NoError(result.Error)
require.NotNil(project.ResourceId)
require.NotEmpty(project.ResourceId)
var reloadProject Project
result = db.First(&reloadProject, &Project{Model: Model{ID: project.ID}})
require.NoError(result.Error)
reloadProject.ResourceId = "NEW VALUE"
result = db.Save(&reloadProject)
require.Error(result.Error)
require.ErrorContains(result.Error, "ResourceId:")
})
t.Run("Adds Vagrantfile", func(t *testing.T) {
require, db := RequireAndDB(t)
vpath := "/dev/null/Vagrantfile"
project := Project{
Name: "default",
Path: "/dev/null",
Basis: TestBasis(t, db),
}
result := db.Save(&project)
require.NoError(result.Error)
v := &Vagrantfile{Path: &vpath}
project.Vagrantfile = v
result = db.Save(&project)
require.NoError(result.Error)
require.NotEmpty(v.ID)
})
t.Run("Updates existing Vagrantfile content", func(t *testing.T) {
require, db := RequireAndDB(t)
// Create inital basis
vpath := "/dev/null/Vagrantfile"
v := &Vagrantfile{Path: &vpath}
project := Project{
Name: "default",
Path: "/dev/null",
Vagrantfile: v,
Basis: TestBasis(t, db),
}
result := db.Save(&project)
require.NoError(result.Error)
require.NotEmpty(v.ID)
originalID := v.ID
// Update with new Vagrantfile
newPath := "/dev/null/new"
newV := &Vagrantfile{Path: &newPath}
project.Vagrantfile = newV
result = db.Save(&project)
require.NoError(result.Error)
require.Equal(*project.Vagrantfile.Path, newPath)
require.Equal(originalID, project.Vagrantfile.ID)
// Refetch Vagrantfile to ensure persisted changes
var checkVF Vagrantfile
result = db.First(&checkVF, &Vagrantfile{Model: Model{ID: originalID}})
require.NoError(result.Error)
require.Equal(*checkVF.Path, newPath)
// Validate only one Vagrantfile has been stored
var count int64
result = db.Model(&Vagrantfile{}).Count(&count)
require.NoError(result.Error)
require.Equal(int64(1), count)
})
}
func TestProject_Delete(t *testing.T) {
t.Run("Deletes project", func(t *testing.T) {
require, db := RequireAndDB(t)
seedProject := TestProject(t, db)
var project Project
result := db.First(&project,
&Project{
Name: seedProject.Name,
Path: seedProject.Path,
},
)
require.NoError(result.Error)
result = db.Where(&Project{ResourceId: project.ResourceId}).
Delete(&Project{})
require.NoError(result.Error)
result = db.First(&Project{}, &Project{ResourceId: project.ResourceId})
require.Error(result.Error)
require.ErrorIs(result.Error, gorm.ErrRecordNotFound)
})
t.Run("Deletes Vagrantfile", func(t *testing.T) {
require, db := RequireAndDB(t)
vpath := "/dev/null/Vagrantfile"
result := db.Save(&Project{
Name: "default",
Path: "/dev/null",
Basis: TestBasis(t, db),
Vagrantfile: &Vagrantfile{Path: &vpath},
})
require.NoError(result.Error)
var count int64
result = db.Model(&Vagrantfile{}).Count(&count)
require.NoError(result.Error)
require.Equal(int64(1), count)
result = db.Where(&Project{Name: "default"}).
Delete(&Basis{})
require.NoError(result.Error)
result = db.Model((*Vagrantfile)(nil)).Count(&count)
require.NoError(result.Error)
require.Equal(int64(0), count)
})
t.Run("Deletes targets", func(t *testing.T) {
require, db := RequireAndDB(t)
result := db.Save(&Project{
Name: "default",
Path: "/dev/null",
Basis: TestBasis(t, db),
Targets: []*Target{
{
Name: "default",
},
{
Name: "Other",
},
},
})
require.NoError(result.Error)
var count int64
result = db.Model(&Project{}).Count(&count)
require.NoError(result.Error)
require.Equal(int64(1), count)
result = db.Model(&Target{}).Count(&count)
require.NoError(result.Error)
require.Equal(int64(2), count)
result = db.Where(&Project{Name: "default"}).
Delete(&Project{})
require.NoError(result.Error)
result = db.Model(&Project{}).Count(&count)
require.NoError(result.Error)
require.Equal(int64(0), count)
result = db.Model(&Target{}).Count(&count)
require.NoError(result.Error)
require.Equal(int64(0), count)
})
}
func TestProject_State(t *testing.T) {
t.Run("Get returns not found error if not exist", func(t *testing.T) {
require := require.New(t)
s := TestState(t)
defer s.Close()
// Set
_, err := s.ProjectGet(&vagrant_plugin_sdk.Ref_Project{
ResourceId: "foo",
})
require.Error(err)
require.Equal(codes.NotFound, status.Code(err))
})
t.Run("Put and Get", func(t *testing.T) {
require := require.New(t)
s := TestState(t)
defer s.Close()
// Set
result, err := s.ProjectPut(TestProject(t, s.db).ToProto())
require.NoError(err)
// Get exact
{
resp, err := s.ProjectGet(&vagrant_plugin_sdk.Ref_Project{
ResourceId: result.ResourceId,
})
require.NoError(err)
require.NotNil(resp)
require.Equal(resp.ResourceId, result.ResourceId)
}
// List
{
resp, err := s.ProjectList()
require.NoError(err)
require.Len(resp, 1)
}
})
t.Run("Delete", func(t *testing.T) {
require := require.New(t)
s := TestState(t)
defer s.Close()
// Set
result, err := s.ProjectPut(TestProject(t, s.db).ToProto())
require.NoError(err)
// Read
resp, err := s.ProjectGet(&vagrant_plugin_sdk.Ref_Project{
ResourceId: result.ResourceId,
})
require.NoError(err)
require.NotNil(resp)
// Delete
{
err := s.ProjectDelete(&vagrant_plugin_sdk.Ref_Project{
ResourceId: result.ResourceId,
})
require.NoError(err)
}
// Read
{
_, err := s.ProjectGet(&vagrant_plugin_sdk.Ref_Project{
ResourceId: result.ResourceId,
})
require.Error(err)
require.Equal(codes.NotFound, status.Code(err))
}
// List
{
resp, err := s.ProjectList()
require.NoError(err)
require.Len(resp, 0)
}
})
}