// Copyright (c) 2023 IBM Corp. // All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package testing import ( "testing" EQT "github.com/IBM/fp-go/eq/testing" F "github.com/IBM/fp-go/function" I "github.com/IBM/fp-go/identity" L "github.com/IBM/fp-go/optics/lens" LI "github.com/IBM/fp-go/optics/lens/iso" O "github.com/IBM/fp-go/option" "github.com/stretchr/testify/assert" ) type ( Street struct { num int name string } Address struct { city string street *Street } Inner struct { Value int Foo string } InnerOpt struct { Value *int Foo *string } Outer struct { inner *Inner } OuterOpt struct { inner *InnerOpt } ) func (outer *OuterOpt) GetInner() *InnerOpt { return outer.inner } func (outer *OuterOpt) SetInner(inner *InnerOpt) *OuterOpt { outer.inner = inner return outer } func (inner *InnerOpt) GetValue() *int { return inner.Value } func (inner *InnerOpt) SetValue(value *int) *InnerOpt { inner.Value = value return inner } func (outer *Outer) GetInner() *Inner { return outer.inner } func (outer *Outer) SetInner(inner *Inner) *Outer { outer.inner = inner return outer } func (inner *Inner) GetValue() int { return inner.Value } func (inner *Inner) SetValue(value int) *Inner { inner.Value = value return inner } func (street *Street) GetName() string { return street.name } func (street *Street) SetName(name string) *Street { street.name = name return street } func (addr *Address) GetStreet() *Street { return addr.street } func (addr *Address) SetStreet(s *Street) *Address { addr.street = s return addr } var ( streetLens = L.MakeLensRef((*Street).GetName, (*Street).SetName) addrLens = L.MakeLensRef((*Address).GetStreet, (*Address).SetStreet) outerLens = L.FromNillableRef(L.MakeLensRef((*Outer).GetInner, (*Outer).SetInner)) valueLens = L.MakeLensRef((*Inner).GetValue, (*Inner).SetValue) outerOptLens = L.FromNillableRef(L.MakeLensRef((*OuterOpt).GetInner, (*OuterOpt).SetInner)) valueOptLens = L.MakeLensRef((*InnerOpt).GetValue, (*InnerOpt).SetValue) sampleStreet = Street{num: 220, name: "Schönaicherstr"} sampleAddress = Address{city: "Böblingen", street: &sampleStreet} sampleStreet2 = Street{num: 220, name: "Neue Str"} defaultInner = Inner{ Value: -1, Foo: "foo", } emptyOuter = Outer{} defaultInnerOpt = InnerOpt{ Value: &defaultInner.Value, Foo: &defaultInner.Foo, } emptyOuterOpt = OuterOpt{} ) func TestStreetLensLaws(t *testing.T) { // some comparison eqs := EQT.Eq[*Street]() eqa := EQT.Eq[string]() laws := AssertLaws( t, eqa, eqs, )(streetLens) cpy := sampleStreet assert.True(t, laws(&sampleStreet, "Neue Str.")) assert.Equal(t, cpy, sampleStreet) } func TestAddrLensLaws(t *testing.T) { // some comparison eqs := EQT.Eq[*Address]() eqa := EQT.Eq[*Street]() laws := AssertLaws( t, eqa, eqs, )(addrLens) cpyAddr := sampleAddress cpyStreet := sampleStreet2 assert.True(t, laws(&sampleAddress, &sampleStreet2)) assert.Equal(t, cpyAddr, sampleAddress) assert.Equal(t, cpyStreet, sampleStreet2) } func TestCompose(t *testing.T) { // some comparison eqs := EQT.Eq[*Address]() eqa := EQT.Eq[string]() streetName := L.Compose[*Address](streetLens)(addrLens) laws := AssertLaws( t, eqa, eqs, )(streetName) cpyAddr := sampleAddress cpyStreet := sampleStreet assert.True(t, laws(&sampleAddress, "Neue Str.")) assert.Equal(t, cpyAddr, sampleAddress) assert.Equal(t, cpyStreet, sampleStreet) } func TestOuterLensLaws(t *testing.T) { // some equal predicates eqValue := EQT.Eq[int]() eqOptValue := O.Eq(eqValue) // lens to access a value from outer valueFromOuter := L.ComposeOption[*Outer, int](&defaultInner)(valueLens)(outerLens) // try to access the value, this should get an option assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuter), O.None[int]())) // update the object withValue := valueFromOuter.Set(O.Some(1))(&emptyOuter) assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuter), O.None[int]())) assert.True(t, eqOptValue.Equals(valueFromOuter.Get(withValue), O.Some(1))) // updating with none should remove the inner nextValue := valueFromOuter.Set(O.None[int]())(withValue) assert.True(t, eqOptValue.Equals(valueFromOuter.Get(nextValue), O.None[int]())) // check if this meets the laws eqOuter := EQT.Eq[*Outer]() laws := AssertLaws( t, eqOptValue, eqOuter, )(valueFromOuter) assert.True(t, laws(&emptyOuter, O.Some(2))) assert.True(t, laws(&emptyOuter, O.None[int]())) assert.True(t, laws(withValue, O.Some(2))) assert.True(t, laws(withValue, O.None[int]())) } func TestOuterOptLensLaws(t *testing.T) { // some equal predicates eqValue := EQT.Eq[int]() eqOptValue := O.Eq(eqValue) intIso := LI.FromNillable[int]() // lens to access a value from outer valueFromOuter := F.Pipe3( valueOptLens, LI.Compose[*InnerOpt](intIso), L.ComposeOptions[*OuterOpt, int](&defaultInnerOpt), I.Ap[L.Lens[*OuterOpt, O.Option[int]]](outerOptLens), ) // try to access the value, this should get an option assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuterOpt), O.None[int]())) // update the object withValue := valueFromOuter.Set(O.Some(1))(&emptyOuterOpt) assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuterOpt), O.None[int]())) assert.True(t, eqOptValue.Equals(valueFromOuter.Get(withValue), O.Some(1))) // updating with none should remove the inner nextValue := valueFromOuter.Set(O.None[int]())(withValue) assert.True(t, eqOptValue.Equals(valueFromOuter.Get(nextValue), O.None[int]())) // check if this meets the laws eqOuter := EQT.Eq[*OuterOpt]() laws := AssertLaws( t, eqOptValue, eqOuter, )(valueFromOuter) assert.True(t, laws(&emptyOuterOpt, O.Some(2))) assert.True(t, laws(&emptyOuterOpt, O.None[int]())) assert.True(t, laws(withValue, O.Some(2))) assert.True(t, laws(withValue, O.None[int]())) }