Standalone yang library data writer
[netconf.git] / apps / yanglib-mdsal-writer / src / test / java / org / opendaylight / netconf / yanglib / writer / YangLibraryWriterTest.java
1 /*
2  * Copyright (c) 2023 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.yanglib.writer;
9
10 import static org.junit.jupiter.api.Assertions.assertEquals;
11 import static org.mockito.ArgumentMatchers.any;
12 import static org.mockito.ArgumentMatchers.eq;
13 import static org.mockito.Mockito.doReturn;
14 import static org.mockito.Mockito.never;
15 import static org.mockito.Mockito.verify;
16 import static org.mockito.Mockito.verifyNoInteractions;
17 import static org.opendaylight.mdsal.common.api.CommitInfo.emptyFluentFuture;
18 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL;
19 import static org.opendaylight.netconf.yanglib.writer.YangLibraryContentBuilderUtil.DEFAULT_MODULE_SET_NAME;
20 import static org.opendaylight.netconf.yanglib.writer.YangLibraryContentBuilderUtil.DEFAULT_SCHEMA_NAME;
21 import static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.Module.ConformanceType.Implement;
22 import static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.Module.ConformanceType.Import;
23 import static org.opendaylight.yangtools.yang.test.util.YangParserTestUtils.parseYangResources;
24
25 import java.util.Optional;
26 import java.util.Set;
27 import java.util.stream.Stream;
28 import org.junit.jupiter.api.BeforeEach;
29 import org.junit.jupiter.api.DisplayName;
30 import org.junit.jupiter.api.Test;
31 import org.junit.jupiter.api.extension.ExtendWith;
32 import org.junit.jupiter.params.ParameterizedTest;
33 import org.junit.jupiter.params.provider.Arguments;
34 import org.junit.jupiter.params.provider.MethodSource;
35 import org.junit.jupiter.params.provider.ValueSource;
36 import org.mockito.ArgumentCaptor;
37 import org.mockito.Captor;
38 import org.mockito.Mock;
39 import org.mockito.junit.jupiter.MockitoExtension;
40 import org.opendaylight.mdsal.binding.api.DataBroker;
41 import org.opendaylight.mdsal.binding.api.WriteTransaction;
42 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.datastores.rev180214.Operational;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.LegacyRevisionUtils;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.ModulesState;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.ModulesStateBuilder;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.RevisionIdentifier;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.RevisionUtils;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.YangLibrary;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.YangLibraryBuilder;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.CommonLeafs;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.module.DeviationBuilder;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.set.parameters.ModuleBuilder;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.set.parameters.module.SubmoduleBuilder;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.DatastoreBuilder;
57 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.ModuleSetBuilder;
58 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.SchemaBuilder;
59 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.YangIdentifier;
60 import org.opendaylight.yangtools.concepts.ListenerRegistration;
61 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
62 import org.opendaylight.yangtools.yang.binding.util.BindingMap;
63 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextListener;
64
65 @ExtendWith(MockitoExtension.class)
66 class YangLibraryWriterTest {
67     private static final YangLibrarySchemaSourceUrlProvider URL_PROVIDER = (moduleSetName, moduleName, revision) ->
68             Optional.of(new Uri("/url/to/" + moduleName + (revision == null ? "" : "/" + revision)));
69     private static final InstanceIdentifier<YangLibrary> YANG_LIBRARY_PATH =
70         InstanceIdentifier.create(YangLibrary.class);
71     private static final InstanceIdentifier<ModulesState> MODULES_STATE_PATH =
72         InstanceIdentifier.create(ModulesState.class);
73     private static final boolean WITH_LEGACY = true;
74     private static final boolean NO_LEGACY = false;
75     private static final boolean WITH_URLS = true;
76     private static final boolean NO_URLS = false;
77
78     @Mock
79     private DOMSchemaService schemaService;
80     @Mock
81     private DataBroker dataBroker;
82     @Mock
83     private WriteTransaction writeTransaction;
84     @Mock
85     private ListenerRegistration<EffectiveModelContextListener> registration;
86     @Mock
87     private YangLibraryWriter.Configuration config;
88     @Captor
89     private ArgumentCaptor<YangLibrary> yangLibraryCaptor;
90     @Captor
91     private ArgumentCaptor<ModulesState> modulesStateCaptor;
92     private YangLibraryWriter writer;
93
94     @BeforeEach
95     void beforeEach() {
96         doReturn(registration).when(schemaService).registerSchemaContextListener(any());
97     }
98
99     private YangLibraryWriter.Configuration setupConfig(final boolean writeLegacy) {
100         doReturn(writeLegacy).when(config).write$_$legacy();
101         return config;
102     }
103
104     @Test
105     @DisplayName("No update bc context has no ietf-yang-library")
106     void noUpdate() {
107         writer = new YangLibraryWriter(schemaService, dataBroker, setupConfig(NO_LEGACY));
108         writer.onModelContextUpdated(parseYangResources(YangLibraryWriterTest.class,
109             "/test-module.yang", "/test-submodule.yang"));
110         verifyNoInteractions(dataBroker);
111     }
112
113     @ParameterizedTest(name = "Write data -- with URLs: {0}, include legacy: {1}")
114     @MethodSource("writeContentArgs")
115     void writeContent(final boolean withUrls, final boolean writeLegacy, final YangLibrary expectedData,
116             final ModulesState expectedLegacyData) {
117         doReturn(writeTransaction).when(dataBroker).newWriteOnlyTransaction();
118         doReturn(emptyFluentFuture()).when(writeTransaction).commit();
119
120         writer = new YangLibraryWriter(schemaService, dataBroker, setupConfig(writeLegacy));
121         if (withUrls) {
122             writer.schemaSourceUrlProvider = URL_PROVIDER;
123         }
124         writer.onModelContextUpdated(parseYangResources(YangLibraryWriterTest.class,
125             "/test-module.yang", "/test-submodule.yang", "/test-more.yang", "/ietf-yang-library.yang"));
126
127         verify(writeTransaction).put(eq(OPERATIONAL), eq(YANG_LIBRARY_PATH), yangLibraryCaptor.capture());
128         assertEquals(expectedData, yangLibraryCaptor.getValue());
129         if (writeLegacy) {
130             verify(writeTransaction).put(eq(OPERATIONAL), eq(MODULES_STATE_PATH), modulesStateCaptor.capture());
131             assertEquals(expectedLegacyData, modulesStateCaptor.getValue());
132         } else {
133             verify(writeTransaction, never()).put(eq(OPERATIONAL), eq(MODULES_STATE_PATH), any());
134         }
135         verify(writeTransaction).commit();
136     }
137
138     private static Stream<Arguments> writeContentArgs() {
139         return Stream.of(
140             Arguments.of(NO_URLS, NO_LEGACY, buildYangLibrary(NO_URLS), null),
141             Arguments.of(NO_URLS, WITH_LEGACY, buildYangLibrary(NO_URLS), buildModulesState(NO_URLS)),
142             Arguments.of(WITH_URLS, NO_LEGACY, buildYangLibrary(WITH_URLS), null),
143             Arguments.of(WITH_URLS, WITH_LEGACY, buildYangLibrary(WITH_URLS), buildModulesState(WITH_URLS)));
144     }
145
146     @ParameterizedTest(name = "Clear data on close -- include legacy: {0}")
147     @ValueSource(booleans = {false, true})
148     void clearOnClose(final boolean writeLegacy) throws Exception {
149         doReturn(writeTransaction).when(dataBroker).newWriteOnlyTransaction();
150         doReturn(emptyFluentFuture()).when(writeTransaction).commit();
151
152         new YangLibraryWriter(schemaService, dataBroker, setupConfig(writeLegacy)).close();
153         verify(writeTransaction).delete(OPERATIONAL, YANG_LIBRARY_PATH);
154         if (writeLegacy) {
155             verify(writeTransaction).delete(OPERATIONAL, MODULES_STATE_PATH);
156         } else {
157             verify(writeTransaction, never()).delete(OPERATIONAL, MODULES_STATE_PATH);
158         }
159         verify(writeTransaction).commit();
160     }
161
162     private static YangLibrary buildYangLibrary(final boolean withUrls) {
163         return new YangLibraryBuilder()
164             .setModuleSet(BindingMap.of(
165                 new ModuleSetBuilder()
166                     .setName(DEFAULT_MODULE_SET_NAME)
167                     .setModule(BindingMap.of(
168                         new ModuleBuilder().setName(new YangIdentifier("test-module_2013-07-22"))
169                             .setNamespace(new Uri("test:namespace"))
170                             .setRevision(new RevisionIdentifier("2013-07-22"))
171                             .setLocation(withUrls ? Set.of(new Uri("/url/to/test-module/2013-07-22")) : null)
172                             .setSubmodule(BindingMap.of(
173                                 new SubmoduleBuilder()
174                                     .setName(new YangIdentifier("test-submodule"))
175                                     .setRevision(RevisionUtils.emptyRevision().getRevisionIdentifier())
176                                     .setLocation(withUrls ? Set.of(new Uri("/url/to/test-submodule")) : null)
177                                     .build()))
178                             .setDeviation(Set.of(new YangIdentifier("test-more_2023-07-25")))
179                             .build(),
180                         new ModuleBuilder().setName(new YangIdentifier("test-more_2023-07-25"))
181                             .setNamespace(new Uri("test:more"))
182                             .setRevision(new RevisionIdentifier("2023-07-25"))
183                             .setLocation(withUrls ? Set.of(new Uri("/url/to/test-more/2023-07-25")) : null)
184                             .setFeature(Set.of(
185                                 new YangIdentifier("first-feature"), new YangIdentifier("second-feature")))
186                             .build(),
187                         new ModuleBuilder().setName(new YangIdentifier("ietf-yang-library_2019-01-04"))
188                             .setNamespace(new Uri("urn:ietf:params:xml:ns:yang:ietf-yang-library"))
189                             .setRevision(new RevisionIdentifier("2019-01-04"))
190                             .setLocation(withUrls ? Set.of(new Uri("/url/to/ietf-yang-library/2019-01-04")) : null)
191                             .build()))
192                     .build()))
193             .setSchema(BindingMap.of(new SchemaBuilder()
194                 .setName(DEFAULT_SCHEMA_NAME)
195                 .setModuleSet(Set.of(DEFAULT_MODULE_SET_NAME))
196                 .build()))
197             .setDatastore(BindingMap.of(
198                 new DatastoreBuilder().setName(Operational.VALUE)
199                     .setSchema(DEFAULT_SCHEMA_NAME)
200                     .build()))
201             .setContentId("1")
202             .build();
203     }
204
205     private static ModulesState buildModulesState(final boolean withUrls) {
206         return new ModulesStateBuilder()
207             .setModule(BindingMap.of(
208                 new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104
209                     .module.list.ModuleBuilder()
210                     .setName(new YangIdentifier("test-module_2013-07-22"))
211                     .setNamespace(new Uri("test:namespace"))
212                     .setRevision(new CommonLeafs.Revision(new RevisionIdentifier("2013-07-22")))
213                     .setSchema(withUrls ? new Uri("/url/to/test-module/2013-07-22") : null)
214                     .setSubmodule(BindingMap.of(
215                         new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104
216                             .module.list.module.SubmoduleBuilder()
217                             .setName(new YangIdentifier("test-submodule"))
218                             .setRevision(LegacyRevisionUtils.emptyRevision())
219                             .setSchema(withUrls ? new Uri("/url/to/test-submodule") : null)
220                             .build()))
221                     .setDeviation(BindingMap.of(
222                         new DeviationBuilder()
223                             .setName(new YangIdentifier("test-more_2023-07-25"))
224                             .setRevision(new CommonLeafs.Revision(new RevisionIdentifier("2023-07-25")))
225                             .build()
226                     ))
227                     .setConformanceType(Import)
228                     .build(),
229                 new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104
230                     .module.list.ModuleBuilder()
231                     .setName(new YangIdentifier("test-more_2023-07-25"))
232                     .setNamespace(new Uri("test:more"))
233                     .setRevision(new CommonLeafs.Revision(new RevisionIdentifier("2023-07-25")))
234                     .setSchema(withUrls ? new Uri("/url/to/test-more/2023-07-25") : null)
235                     .setFeature(Set.of(
236                         new YangIdentifier("first-feature"), new YangIdentifier("second-feature")))
237                     .setConformanceType(Implement)
238                     .build(),
239                 new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104
240                     .module.list.ModuleBuilder()
241                     .setName(new YangIdentifier("ietf-yang-library_2019-01-04"))
242                     .setNamespace(new Uri("urn:ietf:params:xml:ns:yang:ietf-yang-library"))
243                     .setRevision(new CommonLeafs.Revision(new RevisionIdentifier("2019-01-04")))
244                     .setSchema(withUrls ? new Uri("/url/to/ietf-yang-library/2019-01-04") : null)
245                     .setConformanceType(Import)
246                     .build()))
247             .setModuleSetId("1")
248             .build();
249     }
250 }