1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-11-25 22:21:49 +02:00
Files
fp-go/v2/samples/lens/example_test.go

199 lines
6.0 KiB
Go
Raw Normal View History

// Copyright (c) 2023 - 2025 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 lens
import (
"testing"
F "github.com/IBM/fp-go/v2/function"
L "github.com/IBM/fp-go/v2/optics/lens"
"github.com/stretchr/testify/assert"
)
func TestPersonLens(t *testing.T) {
// Create a person
person := Person{
Name: "Alice",
Age: 30,
Email: "alice@example.com",
}
// Create lenses
lenses := MakePersonLenses()
// Test Get
assert.Equal(t, "Alice", lenses.Name.Get(person))
assert.Equal(t, 30, lenses.Age.Get(person))
assert.Equal(t, "alice@example.com", lenses.Email.Get(person))
// Test Set
updated := lenses.Name.Set("Bob")(person)
assert.Equal(t, "Bob", updated.Name)
assert.Equal(t, 30, updated.Age) // Other fields unchanged
assert.Equal(t, "Alice", person.Name) // Original unchanged
// Test Modify
incrementAge := F.Pipe1(
lenses.Age,
L.Modify[Person](func(age int) int { return age + 1 }),
)
incremented := incrementAge(person)
assert.Equal(t, 31, incremented.Age)
assert.Equal(t, 30, person.Age) // Original unchanged
}
func TestCompanyLens(t *testing.T) {
// Create a company with nested structures
company := Company{
Name: "Acme Corp",
Address: Address{
Street: "123 Main St",
City: "Springfield",
ZipCode: "12345",
Country: "USA",
},
CEO: Person{
Name: "John Doe",
Age: 45,
Email: "john@acme.com",
},
}
// Create lenses
companyLenses := MakeCompanyLenses()
addressLenses := MakeAddressLenses()
personLenses := MakePersonLenses()
// Test simple field access
assert.Equal(t, "Acme Corp", companyLenses.Name.Get(company))
// Test nested field access using composition
cityLens := F.Pipe1(
companyLenses.Address,
L.Compose[Company](addressLenses.City),
)
assert.Equal(t, "Springfield", cityLens.Get(company))
// Test nested field update
updatedCompany := cityLens.Set("New York")(company)
assert.Equal(t, "New York", updatedCompany.Address.City)
assert.Equal(t, "Springfield", company.Address.City) // Original unchanged
// Test deeply nested field access
ceoNameLens := F.Pipe1(
companyLenses.CEO,
L.Compose[Company](personLenses.Name),
)
assert.Equal(t, "John Doe", ceoNameLens.Get(company))
// Test deeply nested field update
updatedCompany2 := ceoNameLens.Set("Jane Smith")(company)
assert.Equal(t, "Jane Smith", updatedCompany2.CEO.Name)
assert.Equal(t, "John Doe", company.CEO.Name) // Original unchanged
}
func TestLensComposition(t *testing.T) {
company := Company{
Name: "Tech Inc",
Address: Address{
Street: "456 Oak Ave",
City: "Boston",
ZipCode: "02101",
Country: "USA",
},
CEO: Person{
Name: "Alice Johnson",
Age: 50,
Email: "alice@techinc.com",
},
}
companyLenses := MakeCompanyLenses()
personLenses := MakePersonLenses()
// Compose lenses to access CEO's email
ceoEmailLens := F.Pipe1(
companyLenses.CEO,
L.Compose[Company](personLenses.Email),
)
// Get the CEO's email
email := ceoEmailLens.Get(company)
assert.Equal(t, "alice@techinc.com", email)
// Update the CEO's email
updated := ceoEmailLens.Set("alice.johnson@techinc.com")(company)
assert.Equal(t, "alice.johnson@techinc.com", updated.CEO.Email)
assert.Equal(t, "alice@techinc.com", company.CEO.Email) // Original unchanged
// Modify the CEO's age
ceoAgeLens := F.Pipe1(
companyLenses.CEO,
L.Compose[Company](personLenses.Age),
)
modifyAge := F.Pipe1(
ceoAgeLens,
L.Modify[Company](func(age int) int { return age + 5 }),
)
olderCEO := modifyAge(company)
assert.Equal(t, 55, olderCEO.CEO.Age)
assert.Equal(t, 50, company.CEO.Age) // Original unchanged
}
func TestPersonRefLensesIdempotent(t *testing.T) {
// Create a person pointer
person := &Person{
Name: "Alice",
Age: 30,
Email: "alice@example.com",
}
// Create ref lenses
refLenses := MakePersonRefLenses()
// Test that setting the same value returns the identical pointer (idempotent)
// This works because Name, Age, and Email use MakeLensStrict which has equality optimization
// Test Name field - setting same value should return same pointer
sameName := refLenses.Name.Set("Alice")(person)
assert.Same(t, person, sameName, "Setting Name to same value should return identical pointer")
// Test Age field - setting same value should return same pointer
sameAge := refLenses.Age.Set(30)(person)
assert.Same(t, person, sameAge, "Setting Age to same value should return identical pointer")
// Test Email field - setting same value should return same pointer
sameEmail := refLenses.Email.Set("alice@example.com")(person)
assert.Same(t, person, sameEmail, "Setting Email to same value should return identical pointer")
// Test that setting a different value creates a new pointer
differentName := refLenses.Name.Set("Bob")(person)
assert.NotSame(t, person, differentName, "Setting Name to different value should return new pointer")
assert.Equal(t, "Bob", differentName.Name)
assert.Equal(t, "Alice", person.Name, "Original should be unchanged")
differentAge := refLenses.Age.Set(31)(person)
assert.NotSame(t, person, differentAge, "Setting Age to different value should return new pointer")
assert.Equal(t, 31, differentAge.Age)
assert.Equal(t, 30, person.Age, "Original should be unchanged")
differentEmail := refLenses.Email.Set("bob@example.com")(person)
assert.NotSame(t, person, differentEmail, "Setting Email to different value should return new pointer")
assert.Equal(t, "bob@example.com", differentEmail.Email)
assert.Equal(t, "alice@example.com", person.Email, "Original should be unchanged")
}