- Added a property with table information to session and result set Script types - Added the ability to add new key bindings to the script - Rebuilt the foreground job dispatcher to reduce the occurrence of the progress indicator showing up when no job was running. - Fixed rebinding of keys. Rebinding a key will no longer clear other keys for the old or new bindings.
432 lines
12 KiB
Go
432 lines
12 KiB
Go
package scriptmanager_test
|
|
|
|
import (
|
|
"context"
|
|
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
|
"github.com/lmika/audax/internal/dynamo-browse/models"
|
|
"github.com/lmika/audax/internal/dynamo-browse/services/scriptmanager"
|
|
"github.com/lmika/audax/internal/dynamo-browse/services/scriptmanager/mocks"
|
|
"github.com/pkg/errors"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"testing"
|
|
)
|
|
|
|
func TestModSession_Table(t *testing.T) {
|
|
t.Run("should return details of the current table", func(t *testing.T) {
|
|
tableDef := models.TableInfo{
|
|
Name: "test_table",
|
|
Keys: models.KeyAttribute{
|
|
PartitionKey: "pk",
|
|
SortKey: "sk",
|
|
},
|
|
GSIs: []models.TableGSI{
|
|
{
|
|
Name: "index-1",
|
|
Keys: models.KeyAttribute{
|
|
PartitionKey: "ipk",
|
|
SortKey: "isk",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
rs := models.ResultSet{TableInfo: &tableDef}
|
|
|
|
mockedSessionService := mocks.NewSessionService(t)
|
|
mockedSessionService.EXPECT().ResultSet(mock.Anything).Return(&rs)
|
|
|
|
testFS := testScriptFile(t, "test.tm", `
|
|
table := session.current_table()
|
|
|
|
assert(table.name == "test_table")
|
|
assert(table.keys["partition"] == "pk")
|
|
assert(table.keys["sort"] == "sk")
|
|
assert(len(table.gsis) == 1)
|
|
assert(table.gsis[0].name == "index-1")
|
|
assert(table.gsis[0].keys["partition"] == "ipk")
|
|
assert(table.gsis[0].keys["sort"] == "isk")
|
|
|
|
assert(table == session.result_set().table)
|
|
`)
|
|
|
|
srv := scriptmanager.New(scriptmanager.WithFS(testFS))
|
|
srv.SetIFaces(scriptmanager.Ifaces{
|
|
Session: mockedSessionService,
|
|
})
|
|
|
|
ctx := context.Background()
|
|
err := <-srv.RunAdHocScript(ctx, "test.tm")
|
|
assert.NoError(t, err)
|
|
|
|
mockedSessionService.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("should return nil if no current result set", func(t *testing.T) {
|
|
mockedSessionService := mocks.NewSessionService(t)
|
|
mockedSessionService.EXPECT().ResultSet(mock.Anything).Return(nil)
|
|
|
|
testFS := testScriptFile(t, "test.tm", `
|
|
table := session.current_table()
|
|
|
|
assert(table == nil)
|
|
`)
|
|
|
|
srv := scriptmanager.New(scriptmanager.WithFS(testFS))
|
|
srv.SetIFaces(scriptmanager.Ifaces{
|
|
Session: mockedSessionService,
|
|
})
|
|
|
|
ctx := context.Background()
|
|
err := <-srv.RunAdHocScript(ctx, "test.tm")
|
|
assert.NoError(t, err)
|
|
|
|
mockedSessionService.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestModSession_Query(t *testing.T) {
|
|
t.Run("should successfully return query result", func(t *testing.T) {
|
|
rs := &models.ResultSet{}
|
|
rs.SetItems([]models.Item{
|
|
{"pk": &types.AttributeValueMemberS{Value: "abc"}},
|
|
{"pk": &types.AttributeValueMemberS{Value: "1232"}},
|
|
})
|
|
|
|
mockedSessionService := mocks.NewSessionService(t)
|
|
mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{}).Return(rs, nil)
|
|
|
|
mockedUIService := mocks.NewUIService(t)
|
|
mockedUIService.EXPECT().PrintMessage(mock.Anything, "2")
|
|
mockedUIService.EXPECT().PrintMessage(mock.Anything, "res[0]['pk'].S = abc")
|
|
mockedUIService.EXPECT().PrintMessage(mock.Anything, "res[1]['pk'].S = 1232")
|
|
mockedUIService.EXPECT().PrintMessage(mock.Anything, "res[1].attr('size(pk)') = 4")
|
|
|
|
testFS := testScriptFile(t, "test.tm", `
|
|
res := session.query("some expr").unwrap()
|
|
ui.print(res.length)
|
|
ui.print("res[0]['pk'].S = ", res[0].attr("pk"))
|
|
ui.print("res[1]['pk'].S = ", res[1].attr("pk"))
|
|
ui.print("res[1].attr('size(pk)') = ", res[1].attr("size(pk)"))
|
|
`)
|
|
|
|
srv := scriptmanager.New(scriptmanager.WithFS(testFS))
|
|
srv.SetIFaces(scriptmanager.Ifaces{
|
|
UI: mockedUIService,
|
|
Session: mockedSessionService,
|
|
})
|
|
|
|
ctx := context.Background()
|
|
err := <-srv.RunAdHocScript(ctx, "test.tm")
|
|
assert.NoError(t, err)
|
|
|
|
mockedUIService.AssertExpectations(t)
|
|
mockedSessionService.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("should return error if query returns error", func(t *testing.T) {
|
|
mockedSessionService := mocks.NewSessionService(t)
|
|
mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{}).Return(nil, errors.New("bang"))
|
|
|
|
mockedUIService := mocks.NewUIService(t)
|
|
mockedUIService.EXPECT().PrintMessage(mock.Anything, "true")
|
|
mockedUIService.EXPECT().PrintMessage(mock.Anything, "err(\"bang\")")
|
|
|
|
testFS := testScriptFile(t, "test.tm", `
|
|
res := session.query("some expr")
|
|
ui.print(res.is_err())
|
|
ui.print(res)
|
|
`)
|
|
|
|
srv := scriptmanager.New(scriptmanager.WithFS(testFS))
|
|
srv.SetIFaces(scriptmanager.Ifaces{
|
|
UI: mockedUIService,
|
|
Session: mockedSessionService,
|
|
})
|
|
|
|
ctx := context.Background()
|
|
err := <-srv.RunAdHocScript(ctx, "test.tm")
|
|
assert.NoError(t, err)
|
|
|
|
mockedUIService.AssertExpectations(t)
|
|
mockedSessionService.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("should successfully specify table name", func(t *testing.T) {
|
|
rs := &models.ResultSet{}
|
|
|
|
mockedSessionService := mocks.NewSessionService(t)
|
|
mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{
|
|
TableName: "some-table",
|
|
}).Return(rs, nil)
|
|
|
|
mockedUIService := mocks.NewUIService(t)
|
|
|
|
testFS := testScriptFile(t, "test.tm", `
|
|
res := session.query("some expr", {
|
|
table: "some-table",
|
|
})
|
|
assert(!res.is_err())
|
|
`)
|
|
|
|
srv := scriptmanager.New(scriptmanager.WithFS(testFS))
|
|
srv.SetIFaces(scriptmanager.Ifaces{
|
|
UI: mockedUIService,
|
|
Session: mockedSessionService,
|
|
})
|
|
|
|
ctx := context.Background()
|
|
err := <-srv.RunAdHocScript(ctx, "test.tm")
|
|
assert.NoError(t, err)
|
|
|
|
mockedUIService.AssertExpectations(t)
|
|
mockedSessionService.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("should successfully specify table proxy", func(t *testing.T) {
|
|
rs := &models.ResultSet{}
|
|
|
|
mockedSessionService := mocks.NewSessionService(t)
|
|
mockedSessionService.EXPECT().ResultSet(mock.Anything).Return(&models.ResultSet{
|
|
TableInfo: &models.TableInfo{
|
|
Name: "some-resultset-table",
|
|
},
|
|
})
|
|
mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{
|
|
TableName: "some-resultset-table",
|
|
}).Return(rs, nil)
|
|
|
|
mockedUIService := mocks.NewUIService(t)
|
|
|
|
testFS := testScriptFile(t, "test.tm", `
|
|
res := session.query("some expr", {
|
|
table: session.result_set().table,
|
|
})
|
|
assert(!res.is_err())
|
|
`)
|
|
|
|
srv := scriptmanager.New(scriptmanager.WithFS(testFS))
|
|
srv.SetIFaces(scriptmanager.Ifaces{
|
|
UI: mockedUIService,
|
|
Session: mockedSessionService,
|
|
})
|
|
|
|
ctx := context.Background()
|
|
err := <-srv.RunAdHocScript(ctx, "test.tm")
|
|
assert.NoError(t, err)
|
|
|
|
mockedUIService.AssertExpectations(t)
|
|
mockedSessionService.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("should set placeholder values", func(t *testing.T) {
|
|
rs := &models.ResultSet{}
|
|
|
|
mockedSessionService := mocks.NewSessionService(t)
|
|
mockedSessionService.EXPECT().Query(mock.Anything, ":name = $value", scriptmanager.QueryOptions{
|
|
NamePlaceholders: map[string]string{
|
|
"name": "hello",
|
|
"value": "world",
|
|
},
|
|
ValuePlaceholders: map[string]types.AttributeValue{
|
|
"name": &types.AttributeValueMemberS{Value: "hello"},
|
|
"value": &types.AttributeValueMemberS{Value: "world"},
|
|
},
|
|
}).Return(rs, nil)
|
|
|
|
mockedUIService := mocks.NewUIService(t)
|
|
|
|
testFS := testScriptFile(t, "test.tm", `
|
|
res := session.query(":name = $value", {
|
|
args: {
|
|
name: "hello",
|
|
value: "world",
|
|
},
|
|
})
|
|
assert(!res.is_err())
|
|
`)
|
|
|
|
srv := scriptmanager.New(scriptmanager.WithFS(testFS))
|
|
srv.SetIFaces(scriptmanager.Ifaces{
|
|
UI: mockedUIService,
|
|
Session: mockedSessionService,
|
|
})
|
|
|
|
ctx := context.Background()
|
|
err := <-srv.RunAdHocScript(ctx, "test.tm")
|
|
assert.NoError(t, err)
|
|
|
|
mockedUIService.AssertExpectations(t)
|
|
mockedSessionService.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("should support various placeholder value type", func(t *testing.T) {
|
|
rs := &models.ResultSet{}
|
|
|
|
mockedSessionService := mocks.NewSessionService(t)
|
|
mockedSessionService.EXPECT().Query(mock.Anything, ":name = $value", scriptmanager.QueryOptions{
|
|
NamePlaceholders: map[string]string{
|
|
"str": "hello",
|
|
},
|
|
ValuePlaceholders: map[string]types.AttributeValue{
|
|
"str": &types.AttributeValueMemberS{Value: "hello"},
|
|
"int": &types.AttributeValueMemberN{Value: "123"},
|
|
"float": &types.AttributeValueMemberN{Value: "3.14"},
|
|
"bool": &types.AttributeValueMemberBOOL{Value: true},
|
|
"nil": &types.AttributeValueMemberNULL{Value: true},
|
|
},
|
|
}).Return(rs, nil)
|
|
|
|
mockedUIService := mocks.NewUIService(t)
|
|
|
|
testFS := testScriptFile(t, "test.tm", `
|
|
res := session.query(":name = $value", {
|
|
args: {
|
|
"str": "hello",
|
|
"int": 123,
|
|
"float": 3.14,
|
|
"bool": true,
|
|
"nil": nil,
|
|
},
|
|
})
|
|
assert(!res.is_err())
|
|
`)
|
|
|
|
srv := scriptmanager.New(scriptmanager.WithFS(testFS))
|
|
srv.SetIFaces(scriptmanager.Ifaces{
|
|
UI: mockedUIService,
|
|
Session: mockedSessionService,
|
|
})
|
|
|
|
ctx := context.Background()
|
|
err := <-srv.RunAdHocScript(ctx, "test.tm")
|
|
assert.NoError(t, err)
|
|
|
|
mockedUIService.AssertExpectations(t)
|
|
mockedSessionService.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("should return error when placeholder value type is unsupported", func(t *testing.T) {
|
|
mockedSessionService := mocks.NewSessionService(t)
|
|
mockedUIService := mocks.NewUIService(t)
|
|
|
|
testFS := testScriptFile(t, "test.tm", `
|
|
res := session.query(":name = $value", {
|
|
args: {
|
|
"bad": func() { },
|
|
},
|
|
})
|
|
assert(res.is_err())
|
|
`)
|
|
|
|
srv := scriptmanager.New(scriptmanager.WithFS(testFS))
|
|
srv.SetIFaces(scriptmanager.Ifaces{
|
|
UI: mockedUIService,
|
|
Session: mockedSessionService,
|
|
})
|
|
|
|
ctx := context.Background()
|
|
err := <-srv.RunAdHocScript(ctx, "test.tm")
|
|
assert.Error(t, err)
|
|
|
|
mockedUIService.AssertExpectations(t)
|
|
mockedSessionService.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestModSession_SelectedItem(t *testing.T) {
|
|
t.Run("should return selected item from service implementation", func(t *testing.T) {
|
|
rs := &models.ResultSet{}
|
|
rs.SetItems([]models.Item{
|
|
{"pk": &types.AttributeValueMemberS{Value: "abc"}},
|
|
{"pk": &types.AttributeValueMemberS{Value: "1232"}},
|
|
})
|
|
|
|
mockedSessionService := mocks.NewSessionService(t)
|
|
mockedSessionService.EXPECT().ResultSet(mock.Anything).Return(rs)
|
|
mockedSessionService.EXPECT().SelectedItemIndex(mock.Anything).Return(1)
|
|
|
|
testFS := testScriptFile(t, "test.tm", `
|
|
selItem := session.selected_item()
|
|
|
|
assert(selItem != nil, "selItem != nil")
|
|
assert(selItem.index == 1, "selItem.index")
|
|
assert(selItem.result_set == session.result_set(), "selItem.result_set")
|
|
assert(selItem.attr('pk') == '1232', "selItem.attr('pk')")
|
|
`)
|
|
|
|
srv := scriptmanager.New(scriptmanager.WithFS(testFS))
|
|
srv.SetIFaces(scriptmanager.Ifaces{
|
|
Session: mockedSessionService,
|
|
})
|
|
|
|
ctx := context.Background()
|
|
err := <-srv.RunAdHocScript(ctx, "test.tm")
|
|
assert.NoError(t, err)
|
|
|
|
mockedSessionService.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("should return nil if selected item returns -1", func(t *testing.T) {
|
|
rs := &models.ResultSet{}
|
|
rs.SetItems([]models.Item{
|
|
{"pk": &types.AttributeValueMemberS{Value: "abc"}},
|
|
{"pk": &types.AttributeValueMemberS{Value: "1232"}},
|
|
})
|
|
|
|
mockedSessionService := mocks.NewSessionService(t)
|
|
mockedSessionService.EXPECT().ResultSet(mock.Anything).Return(rs)
|
|
mockedSessionService.EXPECT().SelectedItemIndex(mock.Anything).Return(-1)
|
|
|
|
testFS := testScriptFile(t, "test.tm", `
|
|
selItem := session.selected_item()
|
|
|
|
assert(selItem == nil, "selItem != nil")
|
|
`)
|
|
|
|
srv := scriptmanager.New(scriptmanager.WithFS(testFS))
|
|
srv.SetIFaces(scriptmanager.Ifaces{
|
|
Session: mockedSessionService,
|
|
})
|
|
|
|
ctx := context.Background()
|
|
err := <-srv.RunAdHocScript(ctx, "test.tm")
|
|
assert.NoError(t, err)
|
|
|
|
mockedSessionService.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestModSession_SetResultSet(t *testing.T) {
|
|
t.Run("should set the result set on the session", func(t *testing.T) {
|
|
rs := &models.ResultSet{}
|
|
rs.SetItems([]models.Item{
|
|
{"pk": &types.AttributeValueMemberS{Value: "abc"}},
|
|
{"pk": &types.AttributeValueMemberS{Value: "1232"}},
|
|
})
|
|
|
|
mockedSessionService := mocks.NewSessionService(t)
|
|
mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{}).Return(rs, nil)
|
|
mockedSessionService.EXPECT().SetResultSet(mock.Anything, rs)
|
|
|
|
mockedUIService := mocks.NewUIService(t)
|
|
|
|
testFS := testScriptFile(t, "test.tm", `
|
|
res := session.query("some expr").unwrap()
|
|
session.set_result_set(res)
|
|
`)
|
|
|
|
srv := scriptmanager.New(scriptmanager.WithFS(testFS))
|
|
srv.SetIFaces(scriptmanager.Ifaces{
|
|
UI: mockedUIService,
|
|
Session: mockedSessionService,
|
|
})
|
|
|
|
ctx := context.Background()
|
|
err := <-srv.RunAdHocScript(ctx, "test.tm")
|
|
assert.NoError(t, err)
|
|
|
|
mockedUIService.AssertExpectations(t)
|
|
mockedSessionService.AssertExpectations(t)
|
|
})
|
|
}
|