Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53fd036f58 | ||
|
|
33ba265215 | ||
|
|
7a0fd3762a | ||
|
|
2682c2fa94 | ||
|
|
42aaf3c9c8 | ||
|
|
a40eade67b | ||
|
|
f0b70d7da7 | ||
|
|
4dcabdffed | ||
|
|
de09fc27e9 | ||
|
|
fb4ae6db07
|
||
|
|
2f7025ef28 | ||
|
|
33d993f414
|
||
|
|
87a2218ef6 | ||
|
|
56b6f48a7b
|
||
|
|
80083044a8 | ||
|
|
1811bbf211
|
||
|
|
070e2c95d9 | ||
|
|
7f71fc0566
|
No files matched your search
+10
-61
@@ -1,65 +1,14 @@
|
||||
version: 2.1
|
||||
|
||||
setup: true
|
||||
orbs:
|
||||
qqq-orb: kingsrook/qqq-orb@0.6.9
|
||||
|
||||
commands:
|
||||
configure-package-install:
|
||||
steps:
|
||||
- run:
|
||||
name: Keep package installation unattended
|
||||
command: |
|
||||
# These build machines are discarded after the job.
|
||||
sudo install -d /etc/needrestart/conf.d
|
||||
printf '%s\n' '$nrconf{restart} = "l";' | sudo tee /etc/needrestart/conf.d/99-qqq-ci.conf >/dev/null
|
||||
|
||||
qqq-orb: kingsrook/qqq-orb@0.8
|
||||
workflows:
|
||||
|
||||
test_only:
|
||||
setup:
|
||||
jobs:
|
||||
- qqq-orb/mvn_test_only:
|
||||
pre-steps:
|
||||
- configure-package-install
|
||||
context: [ qqq-maven-registry-credentials ]
|
||||
filters:
|
||||
branches:
|
||||
ignore: /(develop|release/.*|integration.*)/
|
||||
tags:
|
||||
only: []
|
||||
|
||||
publish_snapshot:
|
||||
jobs:
|
||||
- qqq-orb/mvn_publish:
|
||||
pre-steps:
|
||||
- configure-package-install
|
||||
context: [ qqq-maven-registry-credentials ]
|
||||
branch_type: snapshot
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
|
||||
publish_release_candidate:
|
||||
jobs:
|
||||
- qqq-orb/mvn_publish:
|
||||
pre-steps:
|
||||
- configure-package-install
|
||||
context: [ qqq-maven-registry-credentials ]
|
||||
branch_type: release_candidate
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- /release\/.*/
|
||||
|
||||
publish_release:
|
||||
jobs:
|
||||
- qqq-orb/mvn_publish:
|
||||
pre-steps:
|
||||
- configure-package-install
|
||||
context: [ qqq-maven-registry-credentials ]
|
||||
branch_type: release
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: [ /^v\d+\.\d+\.\d+$/ ]
|
||||
- qqq-orb/generate_pipeline:
|
||||
orb_version: '0.8'
|
||||
filters:
|
||||
branches:
|
||||
ignore: automation/qbit-rc-pending
|
||||
tags:
|
||||
only: /^v\d+\.\d+\.\d+$/
|
||||
@@ -0,0 +1,23 @@
|
||||
name: CodeQL
|
||||
'on':
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
- release/**
|
||||
- hotfix/**
|
||||
pull_request: {}
|
||||
schedule:
|
||||
- cron: 41 16 * * 0
|
||||
workflow_dispatch: {}
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
security-events: write
|
||||
packages: read
|
||||
concurrency:
|
||||
group: codeql-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
jobs:
|
||||
analyze:
|
||||
uses: QRun-IO/qqq-orb/.github/workflows/qbit-codeql.yml@1c389210e6dfda1630d053c07ae4dae523dd5576
|
||||
@@ -1,25 +1,74 @@
|
||||
# Cut the next release candidate (QRun-IO/qqq docs/QRUN-QBIT-REPO-STANDARD.md, C6):
|
||||
# run this workflow by hand, or merge a develop PR labelled `release:rc`. It merges develop into
|
||||
# the release line and the release/* push runs CircleCI publish_release_candidate.
|
||||
name: Release candidate
|
||||
|
||||
on:
|
||||
'on':
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: Print the plan without merging or creating branches
|
||||
description: Plan a cut without changing release state
|
||||
type: boolean
|
||||
default: false
|
||||
finalize:
|
||||
description: Retry finalization of an existing cut; do not cut another RC
|
||||
type: boolean
|
||||
default: false
|
||||
recover:
|
||||
description: Recover the same published RC after a deployment timeout; never
|
||||
reupload
|
||||
type: boolean
|
||||
default: false
|
||||
circle_build:
|
||||
description: Original Circle publisher build number (recovery only)
|
||||
type: string
|
||||
default: ''
|
||||
merge_sha:
|
||||
description: Existing cut merge_sha (finalize retry only)
|
||||
type: string
|
||||
default: ''
|
||||
bump_sha:
|
||||
description: Existing cut bump_sha (finalize retry only)
|
||||
type: string
|
||||
default: ''
|
||||
branch:
|
||||
description: Existing cut branch (finalize retry only)
|
||||
type: string
|
||||
default: ''
|
||||
version:
|
||||
description: Existing cut version (finalize retry only)
|
||||
type: string
|
||||
default: ''
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches: [develop]
|
||||
|
||||
types:
|
||||
- closed
|
||||
branches:
|
||||
- develop
|
||||
repository_dispatch:
|
||||
types:
|
||||
- qbit-rc-published
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
actions: read
|
||||
checks: read
|
||||
jobs:
|
||||
release-rc:
|
||||
if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release:rc'))
|
||||
uses: QRun-IO/.github/.github/workflows/qbit-release-rc.yml@main
|
||||
if: github.event_name == 'workflow_dispatch' || github.event_name == 'repository_dispatch'
|
||||
|| (github.event_name == 'pull_request' && github.event.pull_request.merged
|
||||
== true && github.event.pull_request.base.ref == 'develop' && contains(github.event.pull_request.labels.*.name,
|
||||
'release:rc'))
|
||||
uses: QRun-IO/qqq-orb/.github/workflows/qbit-release-rc.yml@1c389210e6dfda1630d053c07ae4dae523dd5576
|
||||
with:
|
||||
dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run }}
|
||||
dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false
|
||||
}}
|
||||
finalize: ${{ github.event_name == 'repository_dispatch' || (github.event_name
|
||||
== 'workflow_dispatch' && inputs.finalize) }}
|
||||
recover: ${{ github.event_name == 'workflow_dispatch' && inputs.recover || false
|
||||
}}
|
||||
circle_build: ${{ github.event_name == 'workflow_dispatch' && inputs.circle_build
|
||||
|| '' }}
|
||||
merge_sha: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.merge_sha
|
||||
|| inputs.merge_sha || '' }}
|
||||
bump_sha: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.bump_sha
|
||||
|| inputs.bump_sha || '' }}
|
||||
branch: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch
|
||||
|| inputs.branch || '' }}
|
||||
version: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.version
|
||||
|| inputs.version || '' }}
|
||||
secrets: inherit
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
profile: qbit
|
||||
orb_version: '0.8'
|
||||
default_branch: develop
|
||||
java_version: '21'
|
||||
browsers: true
|
||||
api_checks: false
|
||||
docs_only: false
|
||||
release_on_main: false
|
||||
release_tag_policy: stable
|
||||
test_branch_policy: non-release-no-integration
|
||||
publish:
|
||||
snapshot: true
|
||||
release_candidate: true
|
||||
release: true
|
||||
asciidoc: false
|
||||
release_completion: true
|
||||
@@ -6,6 +6,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
- QBit metadata now reports the Maven artifact version, including release versions, from a filtered properties resource.
|
||||
|
||||
### Added
|
||||
- Complete host-neutral in-app help bundle with live labels and core `HelpBundleAssertions` coverage; registered through QQQ 4.1.0-RC.7 `HelpBundles`, replacing the temporary loader.
|
||||
- "Check channels" action on Registered Channels; readiness now writes the channel's Slack name, private, bot is member, archived, purpose and checked time back to the row.
|
||||
|
||||
@@ -430,6 +430,11 @@ user with `users.lookupByEmail`, but only when the IdP marked the session email
|
||||
results, and a user sees their own in Preferences.
|
||||
- **Checks run:** every `readinessIntervalMinutes` (15), on the **Check readiness** action, and from
|
||||
`SlackApi.readiness*`. Messages never include token values or exception text.
|
||||
- **Not configured is not a failure.** With no Slack app secrets at all, the `app` check is NOT_CONFIGURED and
|
||||
health is UNKNOWN (HTTP 200) with `configured: false`. Before the first readiness run, health is also UNKNOWN.
|
||||
Only a configured-but-broken app (some secrets missing, or an invalid token key) is DOWN. Slack is optional, so an
|
||||
unset Slack integration must never fail a readiness probe; "not configured" shows in the readiness and
|
||||
central-health views only.
|
||||
- **Health:** when `qqq-middleware-health` is present, the qbit registers `HealthCheckMetaData` `slack`.
|
||||
- It reports the stored deployment-level readiness (`app` and `outbox`), without calling Slack on each
|
||||
probe.
|
||||
|
||||
@@ -120,7 +120,35 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<!-- help bundles carry the artifact version (QQQ help bundle provenance) -->
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>help/*.yaml</include>
|
||||
<include>qbit-slack.properties</include>
|
||||
</includes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>false</filtering>
|
||||
<excludes>
|
||||
<exclude>help/*.yaml</exclude>
|
||||
<exclude>qbit-slack.properties</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<systemPropertyVariables combine.children="append">
|
||||
<qbit.artifactVersion>${project.version}</qbit.artifactVersion>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
|
||||
@@ -21,8 +21,11 @@
|
||||
package com.kingsrook.qbits.slack;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import com.kingsrook.qbits.slack.bridge.SlackOutboxMessagingProviderMetaData;
|
||||
import com.kingsrook.qbits.slack.delivery.SlackAlerts;
|
||||
import com.kingsrook.qbits.slack.metadata.SlackTables;
|
||||
@@ -43,13 +46,41 @@ public class SlackQBitProducer implements QBitMetaDataProducer<SlackQBitConfig>
|
||||
{
|
||||
public static final String GROUP_ID = "com.kingsrook.qbits";
|
||||
public static final String ARTIFACT_ID = "qbit-slack";
|
||||
public static final String VERSION = "0.1.0";
|
||||
public static final String VERSION = loadVersion();
|
||||
|
||||
private SlackQBitConfig qBitConfig;
|
||||
private String namespace;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Read the artifact version from the Maven-filtered properties resource.
|
||||
*******************************************************************************/
|
||||
private static String loadVersion()
|
||||
{
|
||||
try(InputStream inputStream = SlackQBitProducer.class.getClassLoader().getResourceAsStream("qbit-slack.properties"))
|
||||
{
|
||||
if(inputStream == null)
|
||||
{
|
||||
throw (new IllegalStateException("Missing qbit-slack.properties; build with Maven to supply the artifact version"));
|
||||
}
|
||||
Properties properties = new Properties();
|
||||
properties.load(inputStream);
|
||||
String version = properties.getProperty("version");
|
||||
if(version == null || version.isBlank() || version.contains("${"))
|
||||
{
|
||||
throw (new IllegalStateException("Missing or unfiltered version in qbit-slack.properties; build with Maven"));
|
||||
}
|
||||
return (version);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw (new IllegalStateException("Could not read artifact version from qbit-slack.properties", e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for the config.
|
||||
*******************************************************************************/
|
||||
|
||||
@@ -64,9 +64,29 @@ public class SlackHealth implements HealthIndicator
|
||||
public HealthCheckResult check(QInstance instance)
|
||||
{
|
||||
var readiness = SlackReadiness.stored(instance);
|
||||
boolean appFailed = readiness.checks().stream().anyMatch(c -> "app".equals(c.name()) && "ERROR".equals(c.status()));
|
||||
HealthStatus status = appFailed ? HealthStatus.DOWN : readiness.ready() ? HealthStatus.UP : HealthStatus.DEGRADED;
|
||||
String appStatus = readiness.checks().stream().filter(c -> "app".equals(c.name())).map(c -> c.status()).findFirst().orElse("UNKNOWN");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Slack is optional. Not configured (no secrets at all) or not yet checked //
|
||||
// is UNKNOWN, which never fails a readiness probe (HTTP 200). Only a //
|
||||
// configured-but-broken app (partial or invalid secrets) is DOWN. //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
if(SlackReadiness.NOT_CONFIGURED.equals(appStatus))
|
||||
{
|
||||
return (new HealthCheckResult().withStatus(HealthStatus.UNKNOWN)
|
||||
.withDetail("configured", false)
|
||||
.withDetail("message", "Slack is not configured")
|
||||
.withDetail("checks", readiness.checks()));
|
||||
}
|
||||
if("UNKNOWN".equals(appStatus))
|
||||
{
|
||||
return (new HealthCheckResult().withStatus(HealthStatus.UNKNOWN)
|
||||
.withDetail("message", "Slack readiness has not run yet")
|
||||
.withDetail("checks", readiness.checks()));
|
||||
}
|
||||
HealthStatus status = "ERROR".equals(appStatus) ? HealthStatus.DOWN : readiness.ready() ? HealthStatus.UP : HealthStatus.DEGRADED;
|
||||
return (new HealthCheckResult().withStatus(status)
|
||||
.withDetail("configured", true)
|
||||
.withDetail("checks", readiness.checks()));
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
*******************************************************************************/
|
||||
public final class SlackReadiness
|
||||
{
|
||||
public static final String NOT_CONFIGURED = "NOT_CONFIGURED";
|
||||
|
||||
private static final Map<QInstance, SlackApiReadiness> STORED = Collections.synchronizedMap(new WeakHashMap<>());
|
||||
|
||||
private static final Map<QInstance, Map<Integer, SlackApiReadiness>> WORKSPACES = java.util.Collections.synchronizedMap(new java.util.WeakHashMap<>());
|
||||
@@ -87,6 +89,17 @@ public final class SlackReadiness
|
||||
app = false;
|
||||
}
|
||||
List<SlackApiReadinessCheck> checks = new ArrayList<>();
|
||||
if(!app && !anySecretPresent(config))
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// no SLACK_* secret at all: Slack is simply not set up in this //
|
||||
// deployment. That is a state, not a failure; health reports UNKNOWN. //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
checks.add(new SlackApiReadinessCheck("app", NOT_CONFIGURED, "Slack is not configured: no Slack app secrets are set"));
|
||||
SlackApiReadiness result = new SlackApiReadiness(false, checks);
|
||||
STORED.put(instance, result);
|
||||
return (result);
|
||||
}
|
||||
checks.add(check("app", app, "Application configuration"));
|
||||
boolean outbox = true;
|
||||
if(config != null && QContext.getQInstance() == instance)
|
||||
@@ -112,6 +125,33 @@ public final class SlackReadiness
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** True when at least one of the app's secret names resolves to a value.
|
||||
** Presence only; values are never retained.
|
||||
*******************************************************************************/
|
||||
static boolean anySecretPresent(SlackQBitConfig config)
|
||||
{
|
||||
if(config == null)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
for(String name : java.util.Arrays.asList(config.getClientIdEnvName(), config.getClientSecretEnvName(), config.getSigningSecretEnvName(), config.getTokenKeyEnvName()))
|
||||
{
|
||||
try
|
||||
{
|
||||
SlackSecrets.get(config, name);
|
||||
return (true);
|
||||
}
|
||||
catch(RuntimeException e)
|
||||
{
|
||||
// absent or not allowed; keep looking
|
||||
}
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Last scheduled deployment result, without network or database work.
|
||||
*******************************************************************************/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
bundle: qbit-slack
|
||||
version: 0.1.0-SNAPSHOT
|
||||
version: ${project.version}
|
||||
layer: QBIT
|
||||
entries:
|
||||
- key: app:slack;slot:help
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
version=${project.version}
|
||||
@@ -45,6 +45,8 @@ class SlackQBitProducerTest
|
||||
SlackQBitProducer producer = new SlackQBitProducer().withQBitConfig(config).withNamespace("ns");
|
||||
|
||||
QBitMetaData metaData = producer.getQBitMetaData();
|
||||
assertThat(System.getProperty("qbit.artifactVersion")).isNotBlank();
|
||||
assertThat(metaData.getVersion()).isEqualTo(System.getProperty("qbit.artifactVersion")).isEqualTo(SlackQBitProducer.VERSION);
|
||||
assertThat(metaData.getArtifactId()).isEqualTo("qbit-slack");
|
||||
assertThat(metaData.getGroupId()).isEqualTo("com.kingsrook.qbits");
|
||||
assertThat(producer.getNamespace()).isEqualTo("ns");
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.kingsrook.qbits.slack.support.SlackStore;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -62,6 +63,58 @@ class SlackReadinessTest
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** No Slack secrets at all is "not configured": UNKNOWN, never DOWN, so an
|
||||
** optional, unset Slack integration can't fail a readiness probe.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void notConfiguredIsUnknownNotDown() throws Exception
|
||||
{
|
||||
try(SlackTestEnv env = environment())
|
||||
{
|
||||
env.config().withSecretResolver(n -> null);
|
||||
assertThat(SlackReadiness.deployment(env.instance()).checks())
|
||||
.anyMatch(c -> c.name().equals("app") && c.status().equals(SlackReadiness.NOT_CONFIGURED));
|
||||
var result = new SlackHealth().check(env.instance());
|
||||
assertThat(result.getStatus().name()).isEqualTo("UNKNOWN");
|
||||
assertThat(result.getDetails()).containsEntry("configured", false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Before readiness has run, health is UNKNOWN rather than degraded or down.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void notYetCheckedIsUnknown() throws Exception
|
||||
{
|
||||
try(SlackTestEnv env = environment())
|
||||
{
|
||||
QInstance fresh = new QInstance();
|
||||
assertThat(new SlackHealth().check(fresh).getStatus().name()).isEqualTo("UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Partially configured (some secrets set, others missing) is a real
|
||||
** misconfiguration and stays DOWN.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void partiallyConfiguredIsDown() throws Exception
|
||||
{
|
||||
try(SlackTestEnv env = environment())
|
||||
{
|
||||
env.config().withSecretResolver(n -> "SLACK_CLIENT_ID".equals(n) ? "123.456" : null);
|
||||
SlackReadiness.deployment(env.instance());
|
||||
assertThat(new SlackHealth().check(env.instance()).getStatus().name()).isEqualTo("DOWN");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** A valid Base64 string still needs exactly 32 decoded bytes.
|
||||
*******************************************************************************/
|
||||
@@ -166,7 +219,7 @@ class SlackReadinessTest
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Uninitialized readiness maps to degraded health without network access.
|
||||
** Uninitialized readiness maps to UNKNOWN health without network access.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void notReadyHealth() throws Exception
|
||||
@@ -174,7 +227,7 @@ class SlackReadinessTest
|
||||
try(SlackTestEnv env = environment())
|
||||
{
|
||||
assertThat(SlackReadiness.stored(env.instance()).ready()).isFalse();
|
||||
assertThat(new SlackHealth().check(env.instance()).getStatus().name()).isEqualTo("DEGRADED");
|
||||
assertThat(new SlackHealth().check(env.instance()).getStatus().name()).isEqualTo("UNKNOWN");
|
||||
assertThat(env.slack().requests()).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +160,8 @@ class SlackTablesGuardrailTest
|
||||
var bundles = HelpBundles.of(instance).getBundles();
|
||||
assertThat(bundles).hasSize(1);
|
||||
assertThat(bundles.get(0).getId()).isEqualTo("qbit-slack");
|
||||
assertThat(System.getProperty("qbit.artifactVersion")).isNotBlank();
|
||||
assertThat(bundles.get(0).getVersion()).isEqualTo(System.getProperty("qbit.artifactVersion")).isEqualTo(SlackQBitProducer.VERSION);
|
||||
for(String table : SlackTables.ALL)
|
||||
{
|
||||
assertThat(bundles.get(0).getEntries()).filteredOn(entry -> entry.getKey().equals("table:" + table + ";slot:help"))
|
||||
|
||||
Reference in new issue
Block a user