Refactor NetconfDeviceSchemas
[netconf.git] / plugins / netconf-client-mdsal / src / test / java / org / opendaylight / netconf / client / mdsal / impl / SchemaSetupTest.java
1 /*
2  * Copyright (c) 2024 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.client.mdsal.impl;
9
10 import static org.junit.jupiter.api.Assertions.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertSame;
12 import static org.mockito.ArgumentMatchers.any;
13 import static org.mockito.ArgumentMatchers.anyCollection;
14 import static org.mockito.ArgumentMatchers.eq;
15 import static org.mockito.Mockito.doAnswer;
16 import static org.mockito.Mockito.doReturn;
17 import static org.mockito.Mockito.times;
18 import static org.mockito.Mockito.verify;
19
20 import com.google.common.util.concurrent.Futures;
21 import java.net.InetSocketAddress;
22 import java.util.Collection;
23 import java.util.List;
24 import java.util.Set;
25 import org.junit.jupiter.api.Test;
26 import org.junit.jupiter.api.extension.ExtendWith;
27 import org.mockito.ArgumentCaptor;
28 import org.mockito.Mock;
29 import org.mockito.junit.jupiter.MockitoExtension;
30 import org.opendaylight.netconf.client.mdsal.AbstractTestModelTest;
31 import org.opendaylight.netconf.client.mdsal.api.NetconfDeviceSchemas;
32 import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
33 import org.opendaylight.netconf.client.mdsal.api.ProvidedSources;
34 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.available.capabilities.AvailableCapability.CapabilityOrigin;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.available.capabilities.AvailableCapabilityBuilder;
37 import org.opendaylight.yangtools.yang.common.QName;
38 import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
39 import org.opendaylight.yangtools.yang.model.api.source.YangTextSource;
40 import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
41 import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
42 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
43 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
44 import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
45 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
46
47 @ExtendWith(MockitoExtension.class)
48 class SchemaSetupTest extends AbstractTestModelTest {
49     private static final RemoteDeviceId DEVICE_ID = new RemoteDeviceId("someDevice", new InetSocketAddress(42));
50     private static final String TEST_NAMESPACE = "test:namespace";
51     private static final String TEST_MODULE = "test-module";
52     private static final String TEST_REVISION = "2013-07-22";
53     private static final SourceIdentifier TEST_SID = new SourceIdentifier(TEST_MODULE, TEST_REVISION);
54     private static final SourceIdentifier TEST_SID2 = new SourceIdentifier(TEST_MODULE + "2", TEST_REVISION);
55     private static final QName TEST_QNAME = QName.create(TEST_NAMESPACE, TEST_REVISION, TEST_MODULE);
56     private static final QName TEST_QNAME2 = QName.create(TEST_NAMESPACE, TEST_REVISION, TEST_MODULE + "2");
57
58     @Mock
59     private EffectiveModelContextFactory contextFactory;
60     @Mock
61     private SchemaRepository schemaRepository;
62     @Mock
63     private SchemaSourceProvider<YangTextSource> sourceProvider;
64     @Mock
65     private YangTextSource source;
66
67     @Test
68     void testNetconfDeviceFlawedModelFailedResolution() throws Exception {
69         final var ex = new SchemaResolutionException("fail first", TEST_SID, new Throwable("YangTools parser fail"));
70         doAnswer(invocation -> invocation.getArgument(0, Collection.class).size() == 2
71             ? Futures.immediateFailedFuture(ex) : Futures.immediateFuture(SCHEMA_CONTEXT))
72             .when(contextFactory).createEffectiveModelContext(anyCollection());
73
74         doReturn(Futures.immediateFuture(source)).when(schemaRepository)
75             .getSchemaSource(any(), eq(YangTextSource.class));
76
77         final var setup = new SchemaSetup(schemaRepository, contextFactory, DEVICE_ID,
78             new NetconfDeviceSchemas(Set.of(TEST_QNAME, TEST_QNAME2), FeatureSet.builder().build(), Set.of(),
79                 List.of(new ProvidedSources<>(YangTextSource.class, sourceProvider, Set.of(TEST_QNAME, TEST_QNAME2)))),
80             NetconfSessionPreferences.fromStrings(Set.of()));
81
82         final var result = Futures.getDone(setup.startResolution());
83         verify(contextFactory, times(2)).createEffectiveModelContext(anyCollection());
84         assertSame(SCHEMA_CONTEXT, result.modelContext());
85     }
86
87     @Test
88     void testNetconfDeviceMissingSource() throws Exception {
89         // Make fallback attempt to fail due to empty resolved sources
90         final var ex = new MissingSchemaSourceException(TEST_SID, "fail first");
91         doReturn(Futures.immediateFailedFuture(ex))
92                 .when(schemaRepository).getSchemaSource(TEST_SID, YangTextSource.class);
93         doReturn(Futures.immediateFuture(source)).when(schemaRepository)
94             .getSchemaSource(TEST_SID2, YangTextSource.class);
95         doReturn(Futures.immediateFuture(SCHEMA_CONTEXT)).when(contextFactory)
96             .createEffectiveModelContext(anyCollection());
97
98         final var setup = new SchemaSetup(schemaRepository, contextFactory, DEVICE_ID,
99             new NetconfDeviceSchemas(Set.of(TEST_QNAME, TEST_QNAME2), FeatureSet.builder().build(), Set.of(),
100                 List.of(new ProvidedSources<>(YangTextSource.class, sourceProvider, Set.of(TEST_QNAME, TEST_QNAME2)))),
101             NetconfSessionPreferences.fromStrings(Set.of()));
102
103         final var result = Futures.getDone(setup.startResolution());
104         final var captor = ArgumentCaptor.forClass(Collection.class);
105         verify(contextFactory).createEffectiveModelContext(captor.capture());
106         assertEquals(List.of(TEST_SID2), captor.getValue());
107         assertSame(SCHEMA_CONTEXT, result.modelContext());
108     }
109
110     @Test
111     void testNetconfDeviceNotificationsCapabilityIsNotPresent() throws Exception {
112         doReturn(Futures.immediateFuture(source)).when(schemaRepository)
113             .getSchemaSource(any(), eq(YangTextSource.class));
114         doReturn(Futures.immediateFuture(SCHEMA_CONTEXT)).when(contextFactory)
115             .createEffectiveModelContext(anyCollection());
116
117         final var setup = new SchemaSetup(schemaRepository, contextFactory, DEVICE_ID,
118             new NetconfDeviceSchemas(Set.of(TEST_QNAME), FeatureSet.builder().build(), Set.of(),
119                 List.of(new ProvidedSources<>(YangTextSource.class, sourceProvider, Set.of(TEST_QNAME)))),
120             NetconfSessionPreferences.fromStrings(
121                 Set.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION)));
122
123         final var result = Futures.getDone(setup.startResolution());
124         assertEquals(Set.of(new AvailableCapabilityBuilder()
125             .setCapability("(test:namespace?revision=2013-07-22)test-module")
126             .setCapabilityOrigin(CapabilityOrigin.DeviceAdvertised)
127             .build()), result.capabilities().resolvedCapabilities());
128     }
129 }