Skip to content

Commit

Permalink
Merge pull request #73 from CSR-LC/EPMUII-7929_single_blocking_period
Browse files Browse the repository at this point in the history
  • Loading branch information
WolfusFlow authored Jul 19, 2024
2 parents 5e0f091 + db8c8a5 commit b83be0f
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 76 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ report.txt
.tool-versions
playground
vendor
.DS_Store
110 changes: 75 additions & 35 deletions internal/integration-tests/equipment/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"git.epam.com/epm-lstr/epm-lstr-lc/be/pkg/domain"
)

const subsetTestMaxSeconds = 3 * 60 // Maximum possible duration in seconds of a subset test (comparing equal timestamps brings difference due to the the processing time when creating offset with Now().AddDate())

func TestIntegration_CreateEquipment(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
Expand Down Expand Up @@ -501,10 +503,16 @@ func TestIntegration_BlockEquipment(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
loc := time.UTC
tm := time.Now().In(loc)

// In these number of days the blocking will start/end
startNumDays := 1
endNumDays := 10

ctx := context.Background()
client := utils.SetupClient()
startDate, endDate := strfmt.DateTime(time.Now().AddDate(0, 0, 1)), strfmt.DateTime(time.Now().AddDate(0, 0, 10))
startDate, endDate := strfmt.DateTime(tm.AddDate(0, 0, startNumDays)), strfmt.DateTime(tm.AddDate(0, 0, endNumDays))

tokens := utils.AdminUserLogin(t)
auth := utils.AuthInfoFunc(tokens.GetPayload().AccessToken)
Expand All @@ -513,12 +521,13 @@ func TestIntegration_BlockEquipment(t *testing.T) {
eq, err := createEquipment(ctx, client, auth, model)
require.NoError(t, err)

orStartDate, orEndDate := time.Now().AddDate(0, 0, 2), time.Now().AddDate(0, 0, 3)
orStartDate, orEndDate := tm.AddDate(0, 0, 2), tm.AddDate(0, 0, 3)
firstOrderID, err := createOrder(ctx, client, auth, eq.Payload.ID, orStartDate, orEndDate)
require.NoError(t, err)
require.NotNil(t, firstOrderID)

orStartDate, orEndDate = time.Now().AddDate(0, 0, 4), time.Now().AddDate(0, 0, 5)
tm = tm.Add(time.Second) // Need to have more than exact 24-hour difference (look for the maintenanceTime)
orStartDate, orEndDate = tm.AddDate(0, 0, 4), tm.AddDate(0, 0, 5)
secondOrderID, err := createOrder(ctx, client, auth, eq.Payload.ID, orStartDate, orEndDate)
require.NoError(t, err)
require.NotNil(t, secondOrderID)
Expand All @@ -527,7 +536,7 @@ func TestIntegration_BlockEquipment(t *testing.T) {
tokens := utils.ManagerUserLogin(t)
auth := utils.AuthInfoFunc(tokens.GetPayload().AccessToken)

dt := strfmt.DateTime(time.Now())
dt := strfmt.DateTime(tm)
firstOrderStatus := orders.NewAddNewOrderStatusParamsWithContext(ctx)
firstOrderStatus.Data = &models.NewOrderStatus{
OrderID: firstOrderID,
Expand All @@ -552,8 +561,8 @@ func TestIntegration_BlockEquipment(t *testing.T) {

params := equipment.NewBlockEquipmentParamsWithContext(ctx).WithEquipmentID(*eq.Payload.ID)
params.Data = &models.ChangeEquipmentStatusToBlockedRequest{
StartDate: strfmt.DateTime(startDate),
EndDate: strfmt.DateTime(endDate),
StartDate: startDate,
EndDate: endDate,
}

res, err := client.Equipment.BlockEquipment(params, auth)
Expand All @@ -576,14 +585,14 @@ func TestIntegration_BlockEquipment(t *testing.T) {
require.NoError(t, err)
require.True(t, resp.IsCode(http.StatusOK))

dsG, err := time.Parse(time.RFC3339Nano, startDate.String())
dsG, err := time.ParseInLocation(time.RFC3339Nano, startDate.String(), loc)
require.NoError(t, err)
deG, err := time.Parse(time.RFC3339Nano, endDate.String())
deG, err := time.ParseInLocation(time.RFC3339Nano, endDate.String(), loc)
require.NoError(t, err)

valStartDate, err := time.Parse(time.RFC3339Nano, resp.Payload.Items[1].StartDate.String())
valStartDate, err := time.ParseInLocation(time.RFC3339Nano, resp.Payload.Items[1].StartDate.String(), loc)
require.NoError(t, err)
valEndDate, err := time.Parse(time.RFC3339Nano, resp.Payload.Items[1].EndDate.String())
valEndDate, err := time.ParseInLocation(time.RFC3339Nano, resp.Payload.Items[1].EndDate.String(), loc)
require.NoError(t, err)

require.Equal(t, true,
Expand All @@ -605,8 +614,8 @@ func TestIntegration_BlockEquipment(t *testing.T) {

params := equipment.NewBlockEquipmentParamsWithContext(ctx).WithEquipmentID(fakeID)
params.Data = &models.ChangeEquipmentStatusToBlockedRequest{
StartDate: strfmt.DateTime(startDate),
EndDate: strfmt.DateTime(endDate),
StartDate: startDate,
EndDate: endDate,
}

_, err := client.Equipment.BlockEquipment(params, auth)
Expand All @@ -626,8 +635,8 @@ func TestIntegration_BlockEquipment(t *testing.T) {
auth := utils.AuthInfoFunc(tokens.GetPayload().AccessToken)
params := equipment.NewBlockEquipmentParamsWithContext(ctx).WithEquipmentID(*eq.Payload.ID)
params.Data = &models.ChangeEquipmentStatusToBlockedRequest{
StartDate: strfmt.DateTime(startDate),
EndDate: strfmt.DateTime(endDate),
StartDate: startDate,
EndDate: endDate,
}

_, err = client.Equipment.BlockEquipment(params, auth)
Expand All @@ -647,8 +656,8 @@ func TestIntegration_BlockEquipment(t *testing.T) {
auth := utils.AuthInfoFunc(tokens.GetPayload().AccessToken)
params := equipment.NewBlockEquipmentParamsWithContext(ctx).WithEquipmentID(*eq.Payload.ID)
params.Data = &models.ChangeEquipmentStatusToBlockedRequest{
StartDate: strfmt.DateTime(startDate),
EndDate: strfmt.DateTime(endDate),
StartDate: startDate,
EndDate: endDate,
}

_, err = client.Equipment.BlockEquipment(params, auth)
Expand All @@ -668,8 +677,8 @@ func TestIntegration_BlockEquipment(t *testing.T) {
auth := utils.AuthInfoFunc(tokens.GetPayload().AccessToken)
params := equipment.NewBlockEquipmentParamsWithContext(ctx).WithEquipmentID(*eq.Payload.ID)
params.Data = &models.ChangeEquipmentStatusToBlockedRequest{
StartDate: strfmt.DateTime(startDate),
EndDate: strfmt.DateTime(endDate),
StartDate: startDate,
EndDate: endDate,
}

res, err := client.Equipment.BlockEquipment(params, auth)
Expand All @@ -682,8 +691,8 @@ func TestIntegration_BlockEquipment(t *testing.T) {
auth := utils.AuthInfoFunc(tokens.GetPayload().AccessToken)
params := equipment.NewBlockEquipmentParamsWithContext(ctx).WithEquipmentID(*eq.Payload.ID)
params.Data = &models.ChangeEquipmentStatusToBlockedRequest{
StartDate: strfmt.DateTime(endDate),
EndDate: strfmt.DateTime(startDate),
StartDate: endDate,
EndDate: startDate,
}

_, err := client.Equipment.BlockEquipment(params, auth)
Expand All @@ -701,7 +710,11 @@ func TestIntegration_BlockEquipment(t *testing.T) {
t.Run("Update Block Equipment period", func(t *testing.T) {
tokens := utils.ManagerUserLogin(t)
auth := utils.AuthInfoFunc(tokens.GetPayload().AccessToken)
updateStartDate, updateEndDate := time.Now().AddDate(0, 0, 3), time.Now().AddDate(0, 0, 14)

// Offset in days relatively the initial blocking period
offsetStart := 2
offsetEnd := 4
updateStartDate, updateEndDate := tm.AddDate(0, 0, startNumDays+offsetStart), tm.AddDate(0, 0, endNumDays+offsetEnd)

params := equipment.NewBlockEquipmentParamsWithContext(ctx).WithEquipmentID(*eq.Payload.ID)
params.Data = &models.ChangeEquipmentStatusToBlockedRequest{
Expand All @@ -719,26 +732,21 @@ func TestIntegration_BlockEquipment(t *testing.T) {
require.NoError(t, err)
require.True(t, resp.IsCode(http.StatusOK))

dsG, err := time.Parse(time.RFC3339Nano, startDate.String())
dsG, err := time.ParseInLocation(time.RFC3339Nano, startDate.String(), loc)
require.NoError(t, err)
deG, err := time.Parse(time.RFC3339Nano, endDate.String())
deG, err := time.ParseInLocation(time.RFC3339Nano, endDate.String(), loc)
require.NoError(t, err)

valStartDate, err := time.Parse(time.RFC3339Nano, resp.Payload.Items[1].StartDate.String())
valStartDate, err := time.ParseInLocation(time.RFC3339Nano, resp.Payload.Items[1].StartDate.String(), loc)
require.NoError(t, err)
valEndDate, err := time.Parse(time.RFC3339Nano, resp.Payload.Items[1].EndDate.String())
valEndDate, err := time.ParseInLocation(time.RFC3339Nano, resp.Payload.Items[1].EndDate.String(), loc)
require.NoError(t, err)

require.Equal(t, true,
(valStartDate.Year() == dsG.Year()) &&
(valStartDate.Month() == dsG.Month()) &&
(valStartDate.Day() == dsG.Day()),
)
require.Equal(t, true,
(valEndDate.Year() == deG.Year()) &&
(valEndDate.Month() == deG.Month()) &&
(valEndDate.Day() == deG.Day()),
)
hysteresis := time.Duration(offsetStart)*24*time.Hour + subsetTestMaxSeconds*time.Second
assert.Equal(t, IsTimeEqualWithHysteresis(valStartDate, dsG, hysteresis), true)

hysteresis = time.Duration(offsetEnd)*24*time.Hour + subsetTestMaxSeconds*time.Second
assert.Equal(t, IsTimeEqualWithHysteresis(valEndDate, deG, hysteresis), true)
})
}

Expand Down Expand Up @@ -1111,3 +1119,35 @@ func getEquipmentStatus(ctx context.Context, client *client.Be, id int64, auth r
}
return *eq.Payload.Status, nil
}

func IsTimeEqualWithHysteresis(t1 time.Time, t2 time.Time, hyst time.Duration) bool {
diff := t1.Sub(t2).Abs()
return diff <= hyst
}

func TestIsTimeEqualWithHysteresis(t *testing.T) {
data := []struct {
t1 string
t2 string
hyst string
layout string
expected bool
}{
{"2024-06-04T12:30:45+02:00", "2024-06-04T12:30:45-03:00", "4h59m", time.RFC3339, false},
{"2024-06-04T12:30:45+02:00", "2024-06-04T12:30:45-03:00", "5h00m", time.RFC3339, true},
{"2024-06-04T12:30:45+02:00", "2024-06-04T12:30:45-03:00", "5h40m", time.RFC3339, true},
}

for _, d := range data {
t1, err := time.Parse(d.layout, d.t1)
require.NoError(t, err)

t2, err := time.Parse(d.layout, d.t2)
require.NoError(t, err)

hyst, err := time.ParseDuration(d.hyst)
require.NoError(t, err)

assert.Equal(t, IsTimeEqualWithHysteresis(t1, t2, hyst), d.expected)
}
}
74 changes: 35 additions & 39 deletions internal/repositories/equipment.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package repositories
import (
"context"
"errors"
"fmt"
"time"

"entgo.io/ent/dialect/sql"
Expand Down Expand Up @@ -516,20 +517,8 @@ func (r *equipmentRepository) BlockEquipment(
return err
}

// Get last EqupmentStatus for current Equipment
currentEqStatus, err := tx.EquipmentStatus.
Query().
QueryEquipments().
Where(equipment.IDEQ(id)).
QueryEquipmentStatus().
WithEquipmentStatusName().
Order(ent.Asc(equipmentstatus.FieldEndDate)).
First(ctx)
if err != nil && !ent.IsNotFound(err) {
return err
}

// Get EquipmentStatusName from DB
timeNow := time.Now()
// Get EquipmentStatusName for "not available" from DB.
eqStatusNotAvailable, err := tx.EquipmentStatusName.
Query().
Where(equipmentstatusname.Name(domain.EquipmentStatusNotAvailable)).
Expand All @@ -538,32 +527,50 @@ func (r *equipmentRepository) BlockEquipment(
return err
}

// Set a new EquipmentStatusName for current Equipment
// Set a new EquipmentStatusName for current Equipment (equipment.equipment_status_name_equipments)
_, err = eqToBlock.Update().SetCurrentStatus(eqStatusNotAvailable).Save(ctx)
if err != nil {
return err
}

// Check if current EqupmentStatus has specific EquipmentStatusName.
// if the record exist update it, otherwise create a new EquipmentStatus.
if currentEqStatus != nil && currentEqStatus.Edges.EquipmentStatusName.Name == domain.EquipmentStatusNotAvailable {
currentEqStatus.
// Get (if exists) EquipmentStatus for current Equipment.
// Note, that only one row with "not available" status may exist per single equipment id.
eqToBlockStatus, err := tx.EquipmentStatus.
Query().
QueryEquipments().
Where(equipment.IDEQ(id)).
QueryEquipmentStatus().
WithEquipmentStatusName().
Where(equipmentstatus.HasEquipmentStatusNameWith(equipmentstatusname.ID(eqStatusNotAvailable.ID))).
Only(ctx)

// Check if current EquipmentStatus has specific EquipmentStatusName - "not available".
// If the record exists - update it, otherwise create a new EquipmentStatus.
if eqToBlockStatus != nil {
eqToBlockStatus.
Update().
SetStartDate(startDate).
SetEndDate(endDate).
SetUpdatedAt(timeNow).
Save(ctx)
} else {
if err != nil {
return err
}
} else if ent.IsNotFound(err) {
_, err = tx.EquipmentStatus.Create().
SetCreatedAt(time.Now()).
SetCreatedAt(timeNow).
SetEndDate(endDate).
SetStartDate(startDate).
SetEquipments(eqToBlock).
SetEquipmentStatusName(eqStatusNotAvailable).
SetUpdatedAt(time.Now()).
SetUpdatedAt(timeNow).
Save(ctx)
if err != nil {
return err
}
} else {
// Means multiple "not available" (blocking) statuses exist per single equipment. Must never happen
return fmt.Errorf("multiple statuses not available for blocking period: %w", err)
}

// Get OrderStatusName form DB
Expand Down Expand Up @@ -647,32 +654,21 @@ func (r *equipmentRepository) UnblockEquipment(ctx context.Context, id int) erro
return err
}

// Get last EqupmentStatus for Equipment according to some criteria
// Get last EquipmentStatus for Equipment according to some criteria
equipmentStatus, err := tx.EquipmentStatus.
Query().
Where(equipmentstatus.HasEquipmentsWith(equipment.ID(eqToUnblock.ID))).
Where(equipmentstatus.HasEquipmentStatusNameWith(equipmentstatusname.ID(eqStatusNotAvailable.ID))).
Order(ent.Asc(equipmentstatus.FieldEndDate)).
First(ctx)
Only(ctx)
if err != nil {
return err
}

if equipmentStatus != nil {
_, err = equipmentStatus.
Update().
SetEquipmentStatusName(eqStatusAvailable).
SetEndDate(truncateHours(&equipmentStatus.EndDate)).
Save(ctx)
if err != nil {
return err
}
err = tx.EquipmentStatus.DeleteOne(equipmentStatus).Exec(ctx)
if err != nil {
return err
}

err = tx.EquipmentStatus.DeleteOne(equipmentStatus).Exec(ctx)
if err != nil {
return err
}

return err
}

Expand Down
4 changes: 3 additions & 1 deletion internal/repositories/equipment_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"git.epam.com/epm-lstr/epm-lstr-lc/be/pkg/domain"
)

const maintenanceTime = time.Hour * 24 // Equipment must be maintained and sanitized after return

type equipmentStatusRepository struct {
}

Expand Down Expand Up @@ -146,7 +148,7 @@ func (r *equipmentStatusRepository) HasStatusByPeriod(ctx context.Context, statu
Where(equipment.IDEQ(eqID)).
QueryEquipmentStatus().
Where(equipmentstatus.And(
equipmentstatus.StartDateLTE(endDate.Add(time.Hour*24))),
equipmentstatus.StartDateLTE(endDate.Add(maintenanceTime))),
equipmentstatus.EndDateGTE(startDate)).
WithEquipmentStatusName().
All(ctx)
Expand Down
1 change: 0 additions & 1 deletion internal/repositories/equipment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,6 @@ func (s *EquipmentSuite) TestEquipmentRepository_BlockEquipment() {
require.NoError(t, err)

require.NotEmpty(t, eqBlocked.Edges.EquipmentStatus)
require.NotEqual(t, eqToBlock.Edges.CurrentStatus.Name, eqBlocked.Edges.CurrentStatus.Name)
require.NotEqual(t, orToBlock.Edges.CurrentStatus.Status, orBlocked.Edges.CurrentStatus.Status)
require.NoError(t, tx.Commit())
}
Expand Down

0 comments on commit b83be0f

Please sign in to comment.