Skip to content

Commit c5e1250

Browse files
authored
test: legger inn db skjema valideringstest og fikser feil i skjema. (#2604)
1 parent cbcc679 commit c5e1250

File tree

2 files changed

+379
-0
lines changed

2 files changed

+379
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
alter table iay_oppgitt_arbeidsforhold rename constraint "fk_arbeidsforhold_1" TO "fk_oppgitt_arbeidsforhold_1";
2+
3+
alter table diagnostikk_logg rename constraint "diagnostikk_logg_pkey" to "pk_diagnostikk_logg_id";
4+
5+
alter index diagnostikk_logg_saksnummer_idx rename to "idx_diagnostikk_logg_saksnummer";
6+
7+
comment on table diagnostikk_logg is 'Inneholder logger av alle diagnostikk oppslagg fra forvaltning diagnostikk api.';
8+
comment on column diagnostikk_logg.saksnummer is 'Saksnummer som ble slått opp.';
9+
comment on column iay_annen_aktivitet.arbeid_type is 'Arbeidstype';
10+
comment on column iay_arbeidsforhold.handling_type is 'Handlingstype';
11+
comment on column iay_egen_naering.land is 'Land bedriften er registrert i.';
12+
comment on column iay_egen_naering.virksomhet_type is 'Type virksomhet.';
13+
comment on column iay_fravaer.varighet_per_dag is 'Varighet per dag.';
14+
comment on column iay_inntekt.kilde is 'Kilde til inntekten.';
15+
comment on column iay_inntektsmelding.innsendingsaarsak is 'Årsak til innsending av inntektsmelding.';
16+
comment on column iay_oppgitt_arbeidsforhold.land is 'I hvilket land er arbeidsforholdet registrert.';
17+
comment on column lonnskomp_vedtak.fnr is 'Bruker som har fått vedtaket.';
18+
Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
package no.nav.foreldrepenger.abakus.app.database;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import java.util.Arrays;
6+
import java.util.List;
7+
import java.util.stream.Collectors;
8+
9+
import no.nav.foreldrepenger.abakus.dbstoette.JpaExtension;
10+
11+
import org.junit.jupiter.api.DisplayName;
12+
import org.junit.jupiter.api.Test;
13+
import org.junit.jupiter.api.extension.ExtendWith;
14+
15+
import no.nav.vedtak.felles.testutilities.db.EntityManagerAwareTest;
16+
17+
/**
18+
* Tester at alle migreringer følger standarder for navn og god praksis.
19+
*/
20+
@ExtendWith(JpaExtension.class)
21+
public class SjekkDbStrukturTest extends EntityManagerAwareTest {
22+
23+
@Test
24+
@DisplayName("Test at alle tabeller er kommentert.")
25+
void sjekk_at_alle_tabeller_er_dokumentert() {
26+
var sql = """
27+
SELECT
28+
t.table_name
29+
FROM
30+
information_schema.tables t
31+
JOIN
32+
pg_class c ON t.table_name = c.relname
33+
WHERE
34+
t.table_schema = current_schema -- public
35+
AND t.table_name NOT LIKE 'flyway_%'
36+
AND t.table_name NOT LIKE 'prosess_task_partition_%'
37+
AND obj_description(c.oid) IS NULL
38+
""";
39+
40+
var query = getEntityManager().createNativeQuery(sql, String.class);
41+
var avvik = query.getResultStream().toList();
42+
assertThat(avvik).isEmpty();
43+
}
44+
45+
@Test
46+
@DisplayName("Test at alle kolonner er kommentert. Bortsett fra PK og FK kolonner.")
47+
void sjekk_at_alle_relevante_kolonner_er_dokumentert() {
48+
var sql = """
49+
SELECT c.table_name||'.'||c.column_name
50+
FROM pg_catalog.pg_statio_all_tables as st
51+
right join pg_catalog.pg_description pgd on (pgd.objoid=st.relid)
52+
right join information_schema.columns c on (pgd.objsubid=c.ordinal_position
53+
and c.table_schema=st.schemaname and c.table_name=st.relname)
54+
WHERE c.table_schema = current_schema --public
55+
AND upper(c.column_name) NOT IN ('OPPRETTET_TID','ENDRET_TID','OPPRETTET_AV','ENDRET_AV','VERSJON','BESKRIVELSE','NAVN','FOM','TOM','AKTIV')
56+
AND c.table_name not like 'flyway_%'
57+
AND c.table_name not like 'prosess_task_partition_%'
58+
AND c.table_name not like 'prosess_task'
59+
AND pgd.description is null
60+
AND not exists (
61+
SELECT 1
62+
FROM
63+
information_schema.table_constraints AS tc
64+
JOIN information_schema.key_column_usage AS kcu
65+
ON tc.constraint_name = kcu.constraint_name
66+
AND tc.table_schema = kcu.table_schema
67+
WHERE constraint_type IN ('FOREIGN KEY', 'PRIMARY KEY')
68+
AND tc.table_schema = current_schema --public
69+
AND tc.table_name = c.table_name and kcu.column_name = c.column_name
70+
)
71+
ORDER BY c.table_name, c.column_name
72+
""";
73+
74+
var query = getEntityManager().createNativeQuery(sql, String.class);
75+
var avvik = query.getResultStream().map(row -> "\n" + row).toList();
76+
77+
var hjelpetekst = """
78+
Du har nylig lagt til en ny tabell eller kolonne som ikke er dokumentert ihht. gjeldende regler for dokumentasjon.
79+
Vennligst gå over SQL-skriptene og dokumenter tabellene på korrekt måte.
80+
""";
81+
82+
assertThat(avvik).withFailMessage("Mangler dokumentasjon for %s kolonner. %s\n\n%s", avvik.size(), avvik, hjelpetekst).isEmpty();
83+
}
84+
85+
@Test
86+
@DisplayName("Test at alle primary keys har navn med pk_ prefix.")
87+
void skal_ha_primary_key_i_hver_tabell_som_begynner_med_PK() {
88+
var sql = """
89+
SELECT
90+
t.table_name
91+
FROM
92+
information_schema.tables t
93+
WHERE
94+
t.table_schema = current_schema
95+
AND t.table_name NOT LIKE 'flyway_%'
96+
AND t.table_name NOT LIKE 'prosess_task_partition_%'
97+
AND t.table_name NOT IN (
98+
SELECT
99+
c.table_name
100+
FROM
101+
information_schema.table_constraints c
102+
WHERE
103+
c.constraint_type = 'PRIMARY KEY'
104+
AND c.constraint_name LIKE 'pk_%'
105+
);
106+
""";
107+
108+
var query = getEntityManager().createNativeQuery(sql, String.class);
109+
110+
var avvik = query.getResultList();
111+
var tekst = avvik.stream().collect(Collectors.joining("\n"));
112+
var sz = avvik.size();
113+
var feilTekst = "Feil eller mangelende definisjon av primary key (skal hete 'PK_<tabell navn>'). Antall feil = %s \n\nTabell:\n%s";
114+
assertThat(avvik).withFailMessage(feilTekst, sz, tekst).isEmpty();
115+
}
116+
117+
@Test
118+
@DisplayName("Test at alle foreign keys har navn med fk_ prefix.")
119+
void skal_ha_alle_foreign_keys_begynne_med_FK() {
120+
var sql = """
121+
SELECT
122+
table_name,
123+
constraint_name
124+
FROM
125+
information_schema.table_constraints
126+
WHERE
127+
constraint_type = 'FOREIGN KEY'
128+
AND table_schema = current_schema --public
129+
AND constraint_name NOT LIKE 'fk_%';
130+
""";
131+
132+
var query = getEntityManager().createNativeQuery(sql, Object[].class);
133+
List<Object[]> rowList = query.getResultList();
134+
var tekst = rowList.stream()
135+
.map(row -> Arrays.stream(row).map(String.class::cast).collect(Collectors.joining(", ")))
136+
.collect(Collectors.joining("\n"));
137+
var feilTekst = "Feil eller mangelende definisjon av foreign key (skal hete 'FK_<tabell navn>_<løpenummer>'). Antall feil = %s\n\nTabell, Foreign Key\n%s";
138+
assertThat(rowList).withFailMessage(feilTekst, rowList.size(), tekst).isEmpty();
139+
}
140+
141+
@Test
142+
@DisplayName("Test at indexer har riktige prefix: pk_, uidx_, idx_, chk_")
143+
void skal_ha_korrekt_index_navn() {
144+
var sql = """
145+
SELECT
146+
t.relname AS table_name,
147+
i.relname AS index_name,
148+
a.attname AS column_name
149+
FROM
150+
pg_class t,
151+
pg_class i,
152+
pg_index ix,
153+
pg_attribute a,
154+
information_schema.tables it
155+
WHERE
156+
t.oid = ix.indrelid
157+
AND i.oid = ix.indexrelid
158+
AND a.attrelid = t.oid
159+
AND a.attnum = ANY(ix.indkey)
160+
AND t.relkind = 'r'
161+
AND t.relname = it.table_name
162+
AND it.table_schema = current_schema --public
163+
AND it.table_name NOT LIKE 'flyway_%'
164+
AND it.table_name NOT LIKE 'prosess_task_partition_%'
165+
AND i.relname NOT LIKE 'pk_%'
166+
AND i.relname NOT LIKE 'idx_%'
167+
AND i.relname NOT LIKE 'uidx_%'
168+
AND i.relname NOT LIKE 'chk_%'
169+
ORDER BY
170+
t.relname,
171+
i.relname;
172+
""";
173+
174+
var query = getEntityManager().createNativeQuery(sql, Object[].class);
175+
List<Object[]> rowList = query.getResultList();
176+
var tekst = rowList.stream()
177+
.map(row -> Arrays.stream(row).map(String.class::cast).collect(Collectors.joining(", ")))
178+
.collect(Collectors.joining("\n"));
179+
180+
var feilTekst = "Feil navngiving av index. Primary Keys skal ha prefiks PK_, andre unike indekser prefiks UIDX_, vanlige indekser prefiks IDX_, unique constraints CHK_. Antall feil=%s\n\nTabell, Index, Kolonne\n%s";
181+
assertThat(rowList).withFailMessage(feilTekst, rowList.size(), tekst).isEmpty();
182+
}
183+
184+
@Test
185+
@DisplayName("Test at alle FK har like datatype for kolonne på hver side.")
186+
void skal_ha_samme_data_type_for_begge_sider_av_en_FK() {
187+
var sql = """
188+
SELECT
189+
t1.relname,
190+
cs1.column_name,
191+
cs1.data_type,
192+
cs1.character_maximum_length,
193+
cs1.character_octet_length,
194+
cs2.column_name,
195+
cs2.data_type,
196+
cs2.character_maximum_length,
197+
cs2.character_octet_length
198+
FROM
199+
pg_class t1,
200+
pg_class t2,
201+
information_schema.columns cs1,
202+
information_schema.columns cs2,
203+
LATERAL (
204+
SELECT
205+
c.conname,
206+
c.conrelid,
207+
unnest(c.conkey) AS conkeypos,
208+
c.confrelid,
209+
unnest(c.confkey) AS confkeypos
210+
FROM pg_constraint c
211+
WHERE c.contype = 'f'
212+
) AS fk
213+
WHERE
214+
fk.conrelid = t1.oid
215+
AND fk.confrelid = t2.oid
216+
AND t1.relname = cs1.table_name
217+
AND t2.relname = cs2.table_name
218+
AND conkeypos = cs1.ordinal_position
219+
AND confkeypos = cs2.ordinal_position
220+
AND cs1.table_schema = current_schema
221+
AND cs2.table_schema = current_schema
222+
AND cs1.table_name NOT LIKE 'prosess_task_partition_%'
223+
AND cs2.table_name NOT LIKE 'prosess_task_partition_%'
224+
AND (
225+
cs1.data_type != cs2.data_type
226+
OR cs1.character_maximum_length != cs2.character_maximum_length
227+
OR cs1.character_octet_length != cs2.character_octet_length
228+
OR cs1.numeric_precision != cs2.numeric_precision
229+
OR cs1.numeric_precision_radix != cs2.numeric_precision_radix
230+
OR cs1.numeric_scale != cs2.numeric_scale
231+
);
232+
""";
233+
234+
var query = getEntityManager().createNativeQuery(sql, Object[].class);
235+
List<Object[]> rowList = query.getResultList();
236+
var tekst = rowList.stream()
237+
.map(row -> Arrays.stream(row).map(String.class::cast).collect(Collectors.joining(", ")))
238+
.collect(Collectors.joining("\n"));
239+
240+
var feilTekst = "Forskjellig datatype for kolonne på hver side av en FK. Antall feil=%s";
241+
var cols = ".\n\nTABELL, KOL_A, KOL_A_DATA_TYPE, KOL_A_CHAR_LENGTH, KOL_A_CHAR_USED, KOL_B, KOL_B_DATA_TYPE, KOL_B_CHAR_LENGTH, KOL_B_CHAR_USED\n%s";
242+
243+
assertThat(rowList).withFailMessage(feilTekst + cols, rowList.size(), tekst).isEmpty();
244+
}
245+
246+
@Test
247+
@DisplayName("Test at real, float, double precision datatyper ikke brukes.")
248+
void skal_ikke_bruke_FLOAT_REAL_eller_DOUBLEPRECISION() {
249+
var sql = """
250+
SELECT
251+
table_name,
252+
column_name,
253+
data_type
254+
FROM
255+
information_schema.columns
256+
WHERE
257+
table_schema = current_schema
258+
AND data_type IN ('real', 'double precision', 'float');
259+
""";
260+
261+
262+
var query = getEntityManager().createNativeQuery(sql, Object[].class);
263+
List<Object[]> rowList = query.getResultList();
264+
var tekst = rowList.stream()
265+
.map(row -> Arrays.stream(row).map(String.class::cast).collect(Collectors.joining(", ")))
266+
.collect(Collectors.joining("\n"));
267+
268+
var feilTekst = "Feil bruk av datatype, skal ikke ha REAL/FLOAT eller DOUBLE PRECISION (bruk NUMBER for alle desimaltall, spesielt der penger representeres). Antall feil=%s\n\nTabell, Kolonne, Datatype\n%s";
269+
270+
assertThat(rowList).withFailMessage(feilTekst, rowList.size(), tekst).isEmpty();
271+
}
272+
273+
@Test
274+
@DisplayName("Test at alle FK kolonner har en unik index.")
275+
void sjekk_at_alle_FK_kolonner_har_fornuftig_indekser() {
276+
var sql = """
277+
SELECT
278+
uc.table_name,
279+
uc.constraint_name,
280+
STRING_AGG(dcc.column_name, ',' ORDER BY dcc.ordinal_position) AS columns
281+
FROM
282+
information_schema.table_constraints uc
283+
INNER JOIN information_schema.key_column_usage dcc ON dcc.constraint_name = uc.constraint_name
284+
AND dcc.table_schema = uc.table_schema
285+
WHERE
286+
uc.constraint_type = 'FOREIGN KEY'
287+
AND uc.table_schema = current_schema
288+
AND EXISTS (
289+
SELECT
290+
ucc.column_name
291+
FROM
292+
information_schema.key_column_usage ucc
293+
WHERE
294+
ucc.constraint_name = uc.constraint_name
295+
AND uc.table_schema = ucc.table_schema
296+
EXCEPT
297+
SELECT
298+
a.attname AS column_name
299+
FROM
300+
pg_catalog.pg_index ix
301+
JOIN pg_catalog.pg_class t ON t.oid = ix.indrelid
302+
JOIN pg_catalog.pg_class i ON i.oid = ix.indexrelid
303+
JOIN pg_catalog.pg_attribute a ON a.attnum = ANY(ix.indkey) AND a.attrelid = t.oid
304+
WHERE
305+
t.relkind = 'r' -- 'r' means regular table
306+
and t.relname = uc.table_name
307+
)
308+
GROUP BY
309+
uc.table_name,
310+
uc.constraint_name
311+
ORDER BY
312+
uc.table_name;
313+
""";
314+
315+
var query = getEntityManager().createNativeQuery(sql, Object[].class);
316+
List<Object[]> rowList = query.getResultList();
317+
var tekst = rowList.stream()
318+
.map(row -> Arrays.stream(row).map(String.class::cast).collect(Collectors.joining(", ")))
319+
.collect(Collectors.joining("\n"));
320+
321+
var feilTekst = "Kolonner som inngår i Foreign Keys skal ha indeker.\nMangler indekser for %s foreign keys\n%s";
322+
323+
assertThat(rowList).withFailMessage(feilTekst, rowList.size(), tekst).isEmpty();
324+
}
325+
326+
@Test
327+
@DisplayName("Test at alle unike indekser har en uidx_ prefiks.")
328+
void sjekk_at_alle_unke_indexer_starter_med_uidx_prefiks() {
329+
var sql = """
330+
SELECT
331+
tbl.relname AS table_name,
332+
idx.relname AS index_name
333+
FROM
334+
pg_index pgi
335+
JOIN pg_class idx ON idx.oid = pgi.indexrelid
336+
JOIN pg_namespace insp ON insp.oid = idx.relnamespace
337+
JOIN pg_class tbl ON tbl.oid = pgi.indrelid
338+
JOIN pg_namespace tnsp ON tnsp.oid = tbl.relnamespace
339+
WHERE
340+
pgi.indisunique -- Only unique indexes
341+
AND tbl.relname NOT LIKE 'flyway_%'
342+
AND tbl.relname NOT LIKE 'prosess_task_partition_%'
343+
AND idx.relname NOT LIKE 'pk_%'
344+
AND idx.relname NOT LIKE 'uidx_%'
345+
AND tnsp.nspname = current_schema -- Only for tables from the current schema
346+
ORDER BY tbl.relname;
347+
""";
348+
349+
var query = getEntityManager().createNativeQuery(sql, Object[].class);
350+
List<Object[]> rowList = query.getResultList();
351+
var tekst = rowList.stream()
352+
.map(row -> Arrays.stream(row).map(String.class::cast).collect(Collectors.joining(", ")))
353+
.collect(Collectors.joining("\n"));
354+
355+
var feilTekst = "Indekser som er unike skal ha navn som starter med uidx_. Antall feil: %s\n\nTabell, Index\n%s";
356+
357+
assertThat(rowList).withFailMessage(feilTekst, rowList.size(), tekst).isEmpty();
358+
}
359+
360+
}
361+

0 commit comments

Comments
 (0)